"""

.. module:: a2p_service

*Description*:

This module contains the A2P service and API.

*Constants*:

.. seealso:: class :class:`spear.a2pspear_service.A2PSpearService`
"""

# Imports
#
import logging
import time
import urlparse
from datetime import datetime, timedelta, date
from HTMLParser import HTMLParser, HTMLParseError
import uuid
import sys

from datastore.resource import Resource
from datastore.uri import Uri
from runtime.service import Service
from runtime.nodelogger import NodeLogger

# Constants
#

# Classes
#

class A2PServiceAPI():
    """
    The purpose of this class is to provide a functional interface to users of
    the A2P service. Functions are provided for the following A2P requests:
    
    - Get Application List:
        - data from Spear application list page only is copied to response Resource.
    - Get Application:
        - request resolved from Spaces or Spear depending on state of application 
          in Spaces.
    - Get User List:
        - request user list from Spear.
    - Get RA List:
        - request RA list from Spear.
    - Accept/Reject Application:
    - Refer Application:
    - Authenticate Application Attachment:
    
    The returned Resource will have an "error" element if any error occurred
    during the request. This element will contain a String describing the error.
    
    **Constants**:
    
    :param spaces: runtime.spaces.Spaces - Reference to spaces
    """
    
    # Class constants
    #
    ACCEPTREJECT_ACCEPT = 1
    ACCEPTREJECT_ACCEPTWITHCHANGE = 2
    ACCEPTREJECT_REJECT = 3
    
    def __init__(self, spaces):
        # Constructor.
        #
        
        self.logger = NodeLogger.getLogger('eplanning.spear.a2p_service.A2PServiceAPI')
        self.logger.log(logging.DEBUG, "spaces (%s)" % (spaces))
        
        self.is_calledback = False
        self.result_rs = None
        self.spaces = spaces
        
        self.acceptreject_flags = [A2PServiceAPI.ACCEPTREJECT_ACCEPT, 
                                   A2PServiceAPI.ACCEPTREJECT_ACCEPTWITHCHANGE, 
                                   A2PServiceAPI.ACCEPTREJECT_REJECT]
        
    def get_application_list(self, is_current=True):
        """
        Get and return the list of current Spear applications in Spaces.
        
        :param is_current: Set True to return only current applications.
        :rtype: Resource - a Resource containing summary information of all 
            current applications.
        """
        
        self.logger.log(logging.DEBUG, "is_current (%s)" % (is_current))
        
        app_uri_list = self.spaces.find(Uri('spear:app/'))
        apps_dict_dict = {}
        
        for app_uri in app_uri_list:
            app_rs = self.spaces.get(app_uri)
            
            # If an application does not have the 'is_current' flag then assume its false.
            #
            is_current_app = True
            if (app_rs.metadata_has_key(Uri('is_current')) is not True) or \
               (app_rs.get_metadata(Uri('is_current')) is False):
                is_current_app = False
            
            if (is_current == is_current_app):
                self.logger.log(NodeLogger.TRACE, "processing application (%s)" % (app_uri))
                
                sid002b_dict = app_rs.get_var("sid002b_dict")
                
                d = {}
                d["Property_Details"] = sid002b_dict["sid002b_property_details"]
                d["Applicant_Contact"] = sid002b_dict["sid002b_applicant_contact"]
                d["Type"] = sid002b_dict["sid002b_type"]
                d["Status"] = sid002b_dict["sid002b_status"]
                d["Advertised_Indicator"] = sid002b_dict["sid002b_advertised_indicator"]
                d["Objected_Indicator"] = sid002b_dict["sid002b_objected_indicator"]
                d["Appealed_Indicator"] = sid002b_dict["sid002b_appealed_indicator"]
                d["Air_Indicator"] = sid002b_dict["sid002b_air_indicator"]
                d["Council_Hosted_Indicator"] = sid002b_dict["sid002b_council_hosted_indicator"]
                d["spear_ref"] = sid002b_dict["sid002b_spear_ref"]
                d["Council_Ref"] = sid002b_dict["sid002b_council_ref"]
                d["Plan_Number"] = sid002b_dict["sid002b_plan_number"]
                d["Date_Lodged_With_Responsible_Authority"] = sid002b_dict["sid002b_date_lodged_with_responsible_authority"]
                d["Action"] = sid002b_dict["sid002b_action"]
                d["CC_Version"] = sid002b_dict["sid002b_cc_version"]
                d["App_id"] = sid002b_dict["sid002b_app_id"]
                
                d['state'] = A2PService.APPSTATE_NOINFO
                if (app_rs.has_var('control_dict') is True):
                    control_dict = app_rs.get_var('control_dict')
                    if ('state' in control_dict):
                        d['state'] = control_dict['state']
                
                apps_dict_dict[sid002b_dict["sid002b_spear_ref"]] = d
        
        args_dict = {}
        args_dict["apps_dict_dict"] = apps_dict_dict
        self.result_rs = A2PService.create_user_response_resource("", str(uuid.uuid4()), args_dict, error=None)
        
        return self.result_rs
    
    def get_application(self, spear_reference_number):
        """
        Get and return the Spear application with the argument Spear reference
        number.        
        
        :param spear_reference_number: The Spear application reference number.
        :rtype: Resource - a Resource containing the Spear application. 
        """
        
        self.logger.log(logging.DEBUG, "")
        
        # check the 'spear_reference_number' is known.
        #
        app_rs = A2PService.get_application_resource_from_spaces(self.spaces, spear_reference_number)
        if (app_rs is None):
            self.result_rs = Resource(type='term:eplanning/spear/a2p_service/a2presponse')
            self.result_rs.set_var('a2p:request/uri', 'a2puserreq:getapplication')
            self.result_rs.set_var('a2p:request/uuid', str(uuid.uuid4()))
            self.result_rs.set_var("error", "No Spear application found for Spear reference number (%s)." % (spear_reference_number))
        
        else:
            arguments_dict = {}
            arguments_dict["spearReferenceNumber"] = spear_reference_number
            
            rs = Resource(type='term:eplanning/spear/a2p_service/a2prequest')
            rs.set_var('a2p:request/uri', "a2puserreq:getapplication")
            rs.set_var("arguments_dict", arguments_dict)
            response_uri = self.spaces.put(Uri('internal:eplanning/spear/a2p_service/a2prequest'), rs)
            self.spaces.set_watch(response_uri, self.cb_callback, filter=['put'])
            self.wait_for_callback()
        
        return self.result_rs
    
    def get_application_attachment(self, document_id):
        """
        Get and return the Spear application attachment with the argument Spear 
        reference number and document id.        
        
        :param spear_reference_number: The Spear application reference number.
        :param document_id: The attachment document id.
        :rtype: Resource - a Resource containing the Spear application. 
        """
        
        self.logger.log(logging.DEBUG, "")
        
        arguments_dict = {}
        arguments_dict["documentId"] = document_id
        
        rs = Resource(type='term:eplanning/spear/a2p_service/a2prequest')
        rs.set_var('a2p:request/uri', "a2puserreq:getapplicationattachment")
        rs.set_var("arguments_dict", arguments_dict)
        response_uri = self.spaces.put(Uri('internal:eplanning/spear/a2p_service/a2prequest'), rs)
        self.spaces.set_watch(response_uri, self.cb_callback, filter=['put'])
        self.wait_for_callback()
        
        return self.result_rs
    
    def get_user_list(self):
        """
        Get and return the list of council users.
        
        :rtype: Resource - a Resource containing a list of council users.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        arguments_dict = {}
        
        rs = Resource(type='term:eplanning/spear/a2p_service/a2prequest')
        rs.set_var('a2p:request/uri', "a2puserreq:getuserlist")
        rs.set_var("arguments_dict", arguments_dict)
        response_uri = self.spaces.put(Uri('internal:eplanning/spear/a2p_service/a2prequest'), rs)
        self.spaces.set_watch(response_uri, self.cb_callback, filter=['put'])
        self.wait_for_callback()
                
        return self.result_rs
    
    def get_user_list_from_Spear(self):
        """
        Get and return the list of council users from Spear.
        
        :rtype: Resource - a Resource containing a list of council users.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        arguments_dict = {}
        
        rs = Resource(type='term:eplanning/spear/a2p_service/a2prequest')
        rs.set_var('a2p:request/uri', "a2puserreq:getuserlistfromspear")
        rs.set_var("arguments_dict", arguments_dict)
        response_uri = self.spaces.put(Uri('internal:eplanning/spear/a2p_service/a2prequest'), rs)
        self.spaces.set_watch(response_uri, self.cb_callback, filter=['put'])
        self.wait_for_callback()
                
        return self.result_rs
    
    def get_ra_list(self):
        """
        Get and return the list of the council's RAs.
        
        :rtype: Resource - a Resource containing a list of the council's RAs.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        arguments_dict = {}
        
        rs = Resource(type='term:eplanning/spear/a2p_service/a2prequest')
        rs.set_var('a2p:request/uri', "a2puserreq:getralist")
        rs.set_var("arguments_dict", arguments_dict)
        response_uri = self.spaces.put(Uri('internal:eplanning/spear/a2p_service/a2prequest'), rs)
        self.spaces.set_watch(response_uri, self.cb_callback, filter=['put'])
        self.wait_for_callback()
                
        return self.result_rs
    
    def get_ra_list_from_Spear(self):
        """
        Get and return the list of the council's RAs from Spear.
        
        :rtype: Resource - a Resource containing a list of the council's RAs.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        arguments_dict = {}
        
        rs = Resource(type='term:eplanning/spear/a2p_service/a2prequest')
        rs.set_var('a2p:request/uri', "a2puserreq:getralistfromspear")
        rs.set_var("arguments_dict", arguments_dict)
        response_uri = self.spaces.put(Uri('internal:eplanning/spear/a2p_service/a2prequest'), rs)
        self.spaces.set_watch(response_uri, self.cb_callback, filter=['put'])
        self.wait_for_callback()
                
        return self.result_rs
    
    def send_acceptreject_application(self, spear_reference_number, acceptreject_flag=ACCEPTREJECT_ACCEPT, reject_reason=""):
        """
        Send a Spear application accept/reject for the application matching the
        argument 'spear_reference_number'.
        
        :param spear_reference_number: The Spear application reference number.
        :param acceptreject_flag: The type of accept/reject.
        :param reject_reason: The reason for rejection. Mandatory for rejections.
        :rtype: Resource - a Resource.
        """
        
        self.logger.log(logging.DEBUG, "acceptreject_flag (%s)" % acceptreject_flag)
        
        if (acceptreject_flag not in self.acceptreject_flags):
            self.result_rs = Resource(type='term:eplanning/spear/a2p_service/a2presponse')
            self.result_rs.set_var('a2p:request/uri', 'a2puserreq:acceptrejectapplication')
            self.result_rs.set_var('a2p:request/uuid', str(uuid.uuid4()))
            self.result_rs.set_var("error", "Bad Accept/Reject type.")
        
        elif (acceptreject_flag == A2PServiceAPI.ACCEPTREJECT_REJECT) and \
             (len(reject_reason) == 0):
            self.result_rs = Resource(type='term:eplanning/spear/a2p_service/a2presponse')
            self.result_rs.set_var('a2p:request/uri', 'a2puserreq:acceptrejectapplication')
            self.result_rs.set_var('a2p:request/uuid', str(uuid.uuid4()))
            self.result_rs.set_var("error", "Must supply a rejection reason to reject an application.")
        
        else:
            # check the 'spear_reference_number' is known.
            #
            app_rs = A2PService.get_application_resource_from_spaces(self.spaces, spear_reference_number)
            if (app_rs is None):
                self.result_rs = Resource(type='term:eplanning/spear/a2p_service/a2presponse')
                self.result_rs.set_var('a2p:request/uri', 'a2puserreq:acceptrejectapplication')
                self.result_rs.set_var('a2p:request/uuid', str(uuid.uuid4()))
                self.result_rs.set_var("error", "No Spear application found for Spear reference number (%s)." % (spear_reference_number))
            
            else:
#        ACCEPTREJECT_ACCEPT = 1
#        ACCEPTREJECT_ACCEPTWITHCHANGE = 2
#        ACCEPTREJECT_REJECT = 3
                control_dict = app_rs.get_var('control_dict')
                
                arguments_dict = {}
                arguments_dict["spearReferenceNumber"] = spear_reference_number
                arguments_dict["spearAppToken"] = control_dict['spear_app_token']
                arguments_dict['acceptreject_flag'] = acceptreject_flag
                if (acceptreject_flag == A2PServiceAPI.ACCEPTREJECT_REJECT):
                    arguments_dict['reject_reason'] = reject_reason
                
                rs = Resource(type='term:eplanning/spear/a2p_service/a2prequest')
                rs.set_var('a2p:request/uri', "a2puserreq:acceptrejectapplication")
                rs.set_var("arguments_dict", arguments_dict)
                response_uri = self.spaces.put(Uri('internal:eplanning/spear/a2p_service/a2prequest'), rs)
                self.spaces.set_watch(response_uri, self.cb_callback, filter=['put'])
                self.wait_for_callback()
                
        return self.result_rs
    
    def send_application_referral(self):
        """
        Refer a Spear application to one/many RAs.
        
        :rtype: Resource - a Resource.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        arguments_dict = {}
        
        rs = Resource(type='term:eplanning/spear/a2p_service/a2prequest')
        rs.set_var('a2p:request/uri', "a2puserreq:referapplication")
        rs.set_var("arguments_dict", arguments_dict)
        response_uri = self.spaces.put(Uri('internal:eplanning/spear/a2p_service/a2prequest'), rs)
        self.spaces.set_watch(response_uri, self.cb_callback, filter=['put'])
        self.wait_for_callback()
                
        return self.result_rs
    
    def send_application_attachment_authentication(self):
        """
        Authenticate a Spear application attachment.
        
        :rtype: Resource - a Resource.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        arguments_dict = {}
        
        rs = Resource(type='term:eplanning/spear/a2p_service/a2prequest')
        rs.set_var('a2p:request/uri', "a2puserreq:authenticateapplicationattachment")
        rs.set_var("arguments_dict", arguments_dict)
        response_uri = self.spaces.put(Uri('internal:eplanning/spear/a2p_service/a2prequest'), rs)
        self.spaces.set_watch(response_uri, self.cb_callback, filter=['put'])
        self.wait_for_callback()
                
        return self.result_rs
    
    def wait_for_callback(self):
        """
        """
        
        self.is_calledback = False
        
        # wait for response
        #
        while (self.is_calledback is not True):
            self.logger.log(logging.DEBUG, "===================== waiting for callback")
            time.sleep(0.5)
        
        self.is_calledback = False
        
    def cb_callback(self, uri, action, rs):
        """
        """
        
        self.logger.log(logging.DEBUG, "===================== watch callback, uri (%s), action (%s), rs (%s)" % (uri, action, rs))
        
        self.is_calledback = True
        
        if (rs is not None):
            self.spaces.delete(uri)
            
            if (rs.has_var("error") is True) and \
               (len(rs.get_var("error")) > 0):
                self.logger.log(logging.ERROR, "Error (%s)" % rs.get_var("error"))
            
            if (rs.has_var(Uri('a2p:request/uri')) is True):
                request_uri_str = rs.get_var(Uri("a2p:request/uri"))
                if (request_uri_str == "a2puserreq:getapplication"):
                    self.logger.log(logging.DEBUG, "===================== Spear application request response (%s)" % rs.pretty_print())
                elif (request_uri_str == "a2puserreq:getuserlist"):
                    self.logger.log(logging.DEBUG, "===================== Spear user list (%s)" % rs.pretty_print())
                elif (request_uri_str == "a2puserreq:getralist"):
                    self.logger.log(logging.DEBUG, "===================== Spear RA list (%s)" % rs.pretty_print())
                
                self.result_rs = rs

class A2PService(Service):
    """
    The purpose of this class is to accept requests from A2P users and 
    prioritise and queue the requests for processing by cb_process_queues().
    
    A2PService operates from two queues - internal and external. The internal 
    queue is the higher priority queue. All current requests on the internal 
    queue are processed before any request on the external queue.
    
    All user requests are added to the external queue. This class also generates 
    two request types:
    
    - poll requests - these are generated at regular intervals to retrieve new 
      or modified Spear applications for the Council.
    - startup requests - these are generated when the service starts and retrieve
      all current Spear applications for the Council.
    
    The rules for processing requests from A2P user:
    
    - Get Application List:
        - always resolved from Spaces. The 'startup' request will populate Spaces 
          with application Resources. As no request can be performed before the 
          startup request therefore there must always be a set of application Resources
          available.
        - data from Spear application list page only copied to response Resource.
    - Get Application:
        - request resolved from Spaces or Spear depending on state of application 
          in Spaces:
            - if no application in Spaces then:
                - new application Resource created with state = "Updating From Spear" 
                  and saved to Spaces.
                - request made to A2pSpear for application and attachments.
                - application state = "Synchronised With Spear" after valid response
                  received.
                - response sent to user after all application data received.
            - application state = "Summary Info Only":
                - request made to A2pSpear for application and attachments.
            - application state = "Synchronised With Spear" after valid response received.
                - response sent to user after all application data received.
            - application state = "Updating From Spear":
                - application state = "Synchronised With Spear" after valid response received.
                - response sent to user after all application data received.
            - application state = "Expired":
                - application Resource deleted from Spaces.
                - same actions as for no application in Spaces.
            - application state = "Synchronised With Spear":
                - application Resource from Spaces returned to the user.
    - Get User List:
        - request user list from Spear.
    - Get RA List:
        - request RA list from Spear.
    - Accept/Reject Application:
    - Refer Application:
    - Authenticate Application Attachment:
    
    Note: 
        We've already told the user (cb_process_request()) where to look for 
        the response. So it doesn't matter how long it takes to create the 
        response Resource, the user will block waiting for it.
    
    Rules for sending requests to A2pSpear:
    
    - Get Application List:
        - performed once at startup and regularly during polling, automatically, 
          by A2PService.
            - request application list page (sid002b) only.
            - create application Resources from response, only if app. does not
              yet exist.
                - Application Resource state = "Summary Info Only".
        - additions to the list are picked up during polling of Spear activity log.
    - Get Application:
        - this is performed as a result of activity log processing and (maybe) 
          user requests for an application.
        - request application details page (sid004).
        - request all attachments (sid005a*, sid006a, sid072a, sid061a).
    - Get Application Attachment:
        - this is performed as a result of activity log processing. 
        - request application attachment PDF.
    - Get Activity Log:
        - performed periodically by A2PService, based on timer.
            - start date taken from A2p configuration Resource.
            - end date never sent (now() is assumed by Spear).
        - date/time of request is stored in A2p configuration Resource (stored in DS).
        - result is parsed for unique Spear Reference Numbers only. These reflect 
          those applications that have changed since the last activity log request. 
          Other log information is ignored. For each Spear Reference Number a "Get 
          Application" request is generated and sent to A2pSpear.
    
    **Constants**:
    
    .. data:: APPSTATE_SUMMARYINFOONLY
    
        APPSTATE_SUMMARYINFOONLY is the state of an application Resource
        after the response for request 'spear:request/currentapplicationlist'
        (sid002b) has been processed.
    
    .. data:: APPSTATE_UPDATINGFROMSPEAR
    
        APPSTATE_UPDATINGFROMSPEAR is the state of an application Resource
        while application (sid004) and attachment data (sid005a, sid006a,
        sid072a, sid061a, sid075c, pdf) is being retrieved from Spear.
    
    .. data:: APPSTATE_EXPIRED
    
        APPSTATE_EXPIRED is the state of an application Resource after it
        has existed in the Adaptor for too long.
    
    .. data:: APPSTATE_INSYNCWITHSPEAR
    
        APPSTATE_INSYNCWITHSPEAR is the state of an application Resource
        when it has been fully updated from Spear. 
    
    :param spaces: runtime.spaces.Spaces - Reference to spaces
    :param ds: datastore.database.DataStore
    :param service_uri: String - the uri for this service. 
    """
    
    # Class constants
    #
    APPSTATE_NOINFO = "noinfo"
    APPSTATE_SUMMARYINFOONLY = "summaryinfoonly"
    APPSTATE_UPDATINGFROMSPEAR = "updatingfromspear"
    APPSTATE_EXPIRED = "expired"
    APPSTATE_INSYNCWITHSPEAR = "insyncwithspear"
    
    def __init__(self, spaces, ds, service_uri):
        # Constructor.
        #
        
        Service.__init__(self, spaces=spaces, datastore=ds, uri=service_uri, node_start=False)
        
        self.logger = NodeLogger.getLogger('eplanning.spear.a2p_service.A2PService')
        self.logger.log(logging.DEBUG, "spaces (%s), ds (%s), service_uri (%s)" % (spaces, ds, service_uri))
        
        # Dictionary of request context dictionaries, keyed by UUID. This dictionary 
        # (of dictionaries) ties together requests from the user to responses from A2PSpear.
        #
        self.request_context_dict_dict = {}
        
        # Load Spear static data from spaces.
        #
        self.statics = self.spaces.get(Uri('term:eplanning/spear/a2p_service/spear_statics'))
            
    def cb_start(self, uri, action, rs):
        """
        This function is called when the task starts. 
        
        :param uri: Uri - The service Uri.
        :param action: String - spaces action, probably 'put'.
        :param rs: Resource - This service's definition Resource.
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), action (%s), rs (%s)" % (uri, action, rs))
        
        # start the poll timer.
        #
        cron_resource = Resource(type='term:base/services/time/timer')
        cron_resource.set_var("dc:title", "A2P Cron Timer")
        cron_resource.set_var("interval", self.statics.get_var_part('content.poll_timer'))
        cron_resource.set_var("iterations", sys.maxint)
#        cron_uri = self.spaces.put(Uri('internal:base/services-list/time_service/timer/request'), cron_resource)
        cron_uri = self.spaces.put(Uri('internal:base/services/time_service/timer/'), cron_resource)
        
        self.spaces.set_watch(cron_uri + "response", self.cb_cron, ["put"])
        self.spaces.set_process(self.service_def.api.a2prequest.uri, self.cb_process_request)
        
        # start the task to process the queues
        #
        self.process_thread = self.async(self.cb_process_queues, uri=None, action=None, res=None, daemon=True, name="Async A2P")
        
        # Allocate a UUID that we'll use to tie requests with responses. 
        #
        uuid_str = str(uuid.uuid4())
        
        # Create a new Startup request Resource and send to A2pProcessThread.
        #
        rs = self.create_queue_request_resource('a2preq:startup', uuid_str, arguments_dict={})
        self.add_request_to_internal_queue(rs)
        
    def cb_stop(self, uri, action, rs):
        """
        This function will stop any listeners still running.
        
        :param uri: Uri - The service Uri.
        :param action: String - Spaces action.
        :param rs: Resource - A resource is provided for 'get', 'put' and 'remove'.
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), action (%s), rs (%s)" % (uri, action, rs))
        
        self.process_thread.cancel()
        
    def cb_cron(self, uri, action, rs):
        """
        This function is called when the cron timer goes off. Here we create a
        poll request and send it to ourselves.
        
        Rules for polling:
            - we poll Spear for the Spear activity log.
            - save last poll timestamp.
            - when processing log events:
                - extract Spear reference number only. Assume an event on a Spear
                  application is a modification to that application.
                - ignore all other log event fields.
                - we might catch events for deleted applications. 
                - de-dup extracted Spear reference numbers.
            - day zero:
                - assume saved last poll timestamp is now().
            - day zero and startup:
                - get list of current applications
                - poll from now.
        
        Therefore:
            - day zero and startup:
                - get list of current applications
                - start polling of the Spear activity log.
            - polling:
                - driven by cron timer, timer events popping regularly.
            - request Spear activity log for the period equal to the period of
              the polling cron timer.
                - therefore we have no need to save the last poll timestamp.
            - process activity log
                - filter for Spear reference numbers only. De-dup.
        
        :param uri: Uri - The service Uri.
        :param action: String - Spaces action.
        :param rs: Resource - A resource is provided for 'get', 'put' and 'remove'.
        """
        
        # TODO: can be turned off (Spear or adaptor?)
        
        self.logger.log(logging.DEBUG, "uri (%s), action (%s), rs (%s)" % (uri, action, rs))
        
        self.spaces.delete(uri)
        
        # Get the last poll time from the control Resource.
        #
        dt_poll_start = None
        control_rs = A2PService.get_a2p_control_resource_from_spaces(self.spaces)
        if (control_rs is not None):
            last_poll_time_str = control_rs.get_var('last_poll_time')
            if (last_poll_time_str is not None) and \
               (len(last_poll_time_str) > 0):
                dt_poll_start = datetime.strptime(last_poll_time_str, "%d/%m/%Y %H:%M:%S")
        
        if (dt_poll_start is None):
            dt_now = datetime.now() 
            dt_poll_start = dt_now - timedelta(seconds=self.statics.get_var_part('content.poll_timer'))
        
        # Allocate a UUID that we'll use to tie requests with responses. 
        #
        uuid_str = str(uuid.uuid4())
        
        args_dict = {}
        args_dict["startDate"] = dt_poll_start.strftime("%Y%m%dT%H%M%S")
        rs = self.create_queue_request_resource('a2preq:poll', uuid_str, arguments_dict=args_dict)
        
        # If there's already a POLL request on the queue then don't double-up.
        #
        if (self.is_request_in_queue(self.service_def.api.queues.external_queue_uri, rs) is False):
            self.logger.log(logging.DEBUG, "adding poll request to external queue")
            self.add_request_to_external_queue(rs)
        
        # Update 'last_poll_time' with current time.
        #
        self.create_a2p_control_resource_in_spaces(datetime.now())
        
    def cb_process_request(self, uri, action, rs):
        """
        This function is called to process user requests. Here we decide 
        which A2P request (ie. which Spear request) corresponds to the user's
        request and send the A2P request to A2PService. 
        
        The Uri denotes an action, not an object. The action never exists so 
        we always return False for 'exists' requests from Spaces.
        
        :param uri: Uri - The service Uri.
        :param action: String - Spaces action.
        :param rs: Resource - A resource is provided for 'get', 'put' and 'remove'.
        """
        
        self.logger.log(logging.DEBUG, "uri (%s), action (%s), rs (%s)" % (uri, action, rs))
        
        if (action is not None) and \
           ((action == 'exists') or \
            (action == 'delete')):
            return False
        
        self.spaces.delete(uri)
        
        self.logger.log(NodeLogger.TRACE, "uri (%s), action (%s)" % (uri, action))
        self.logger.log(NodeLogger.TRACE, "uri (%s), action (%s), rs (%s)" % (uri, action, rs.pretty_print()))
        
        # find the list of Spear instructions matching the user's request.
        #
        a2p_request_uri = rs.get_var(Uri('a2p:request/uri'))
        if (a2p_request_uri is not None):
            # Allocate a UUID that we'll use to tie requests with responses. 
            #
            uuid_str = str(uuid.uuid4())
            response_uri = Uri('internal:eplanning/spear/a2pspear_service/a2presponse') + uuid_str
            
            # Create a new Resource that contains the A2PSpear request and response Uri, and send to A2pProcessThread.
            #
            a2pspear_rs = self.create_queue_request_resource(a2p_request_uri, uuid_str, rs.get_var("arguments_dict"), response_uri)
            self.add_request_to_external_queue(a2pspear_rs)
            
            return response_uri
        
        self.logger.log(logging.WARNING, "request does not contain an A2PUser request")
        return ""
        
    def create_queue_request_resource(self, request_uri, uuid_str, arguments_dict, response_uri=None):
        """
        This function creates the Resource we send to A2PProcessThread.
        
        :param request_uri: Uri - the user's request Uri
        :param uuid_str: String - the UUID for this request
        :param arguments_dict: Dictionary - arguments for the request
        :param response_uri: Uri - the Uri that this request should respond to
        :rtype: Resource - the created Resource
        """
        
        control_dict = {}
        control_dict['a2p:request/uuid'] = uuid_str
        control_dict['a2p:response/uri'] = response_uri
        control_dict["arguments_dict"] = arguments_dict
        
        # Create a new Resource that contains the A2PSpear request.
        #
        rs = Resource(type='term:eplanning/spear/a2p_service/a2pqueuerequest')
        rs.set_var('a2p:request/uri', request_uri)
        rs.set_var("control_dict", control_dict)
        
#        # Allocate a new request context.
#        #
#        self.request_context_dict_dict[uuid_str] = {}
#        self.request_context_dict_dict[uuid_str][Uri('a2p:request/uri')] = request_uri

        return rs
    
    def add_request_to_internal_queue(self, request_rs):
        """
        This function adds the argument Resource to the A2P internal queue.
        
        Note: 
            We do NOT check to see if 'request_rs' already exists on the
            queue because its likely that its request Uri 'a2p:request/uri' DOES
            exist on the queue (eg. requests for 'a2preq:getattachmentmetadata'
            are very common), however the request's other data is probably different.
            Any uniqueness test must be performed elsewhere.
        
        :param request_rs: Resource - Request to add to queue.
        """
        
        self.spaces.put(self.service_def.api.queues.internal_queue_uri, request_rs)
    
    def add_request_to_external_queue(self, request_rs):
        """
        This function adds the argument Resource to the A2P external queue.
        
        Note: 
            We do NOT check to see if 'request_rs' already exists on the
            queue because its likely that its request Uri 'a2p:request/uri' DOES
            exist on the queue (eg. requests for 'a2preq:getattachmentmetadata'
            are very common), however the request's other data is probably different.
            Any uniqueness test must be performed elsewhere.
        
        :param request_rs: Resource - Request to add to queue.
        """
        
        self.spaces.put(self.service_def.api.queues.external_queue_uri, request_rs)
    
    def is_request_in_queue(self, queue_uri, request_rs):
        """
        This function searches the argument queue Uri for the argument Resource.
        Matching is based on the value of 'spear:request/uri'.
        
        :param queue_uri: Uri - uri of queue in Spaces
        :param request_rs: Resource - Is this Resource in the queue?
        :rtype: Boolean - True if Resource found in queue, otherwise False.
        """
        
        request_uri = request_rs.get_var(Uri('a2p:request/uri'))
        queue_rs_uri_list = self.spaces.get_collection(queue_uri)
        if (queue_rs_uri_list is not None):
            for uri in queue_rs_uri_list:
                rs = self.spaces.get(uri)
                queued_request_uri = rs.get_var(Uri('a2p:request/uri'))
                if (str(queued_request_uri) == str(request_uri)):
                    return True
        
        return False

    def cb_process_queues(self, uri, action, rs):
        """
        This function is called from Service::async() and runs in a new Thread 
        created by Service::async(). This is the mainline of the thread.
        
        Loop, waiting on requests on both internal and external queues. Exit
        if task being shutdown.
        
        As we have 2 queues to wait on we can't block on either. Next best thing
        is to sleep and wake-up regularly to check the queues. 
        
        We first check the internal queue. Only if its empty do we check the
        external queue. Otherwise we keep processing internal queue requests
        until there are no more. After processing any external queue request
        we must again check the internal queue. Remember that get-application
        requests can generate requests for attachments - these requests are 
        placed on the internal queue.
        
        :param uri: Uri - The service Uri.
        :param action: String - spaces action.
        :param rs: Resource - A resource is provided for 'get', 'put' and 'remove'.
        """
        
        self.logger = NodeLogger.getLogger('eplanning.spear.a2p_service.A2PService')
        self.logger.log(logging.DEBUG, "cb_process_queues() started")
        
        while (True):
            request_rs = None
            
            request_rs_uri_list = self.spaces.get_collection(self.service_def.api.queues.internal_queue_uri)
            if (request_rs_uri_list is None) or (len(request_rs_uri_list) == 0):
                self.logger.log(NodeLogger.TRACE, "internal queue empty, checking external queue")
                request_rs_uri_list = self.spaces.get_collection(self.service_def.api.queues.external_queue_uri)
                if (request_rs_uri_list is None) or (len(request_rs_uri_list) == 0):
                    self.logger.log(NodeLogger.TRACE, "external queue empty")
                else:
                    self.logger.log(NodeLogger.TRACE, "processing external queue requests (%s)" % request_rs_uri_list)
            else:
                self.logger.log(NodeLogger.TRACE, "processing internal queue requests (%s)" % request_rs_uri_list)
            
            if (request_rs_uri_list is not None) and (len(request_rs_uri_list) > 0):
                request_rs_uri = request_rs_uri_list[0]
                request_rs = self.spaces.remove(request_rs_uri)
            
            self.logger.log(logging.DEBUG, "Main: (%s)" % (request_rs))
            
            if (request_rs is not None):
                request_uri = request_rs.get_var(Uri('a2p:request/uri'))
                request_uri_str = str(request_uri)
                
                req_control_dict = request_rs.get_var("control_dict")
                arguments_dict = req_control_dict["arguments_dict"]
                
                app_rs = None
                if ("spearReferenceNumber" in arguments_dict):
                    app_rs = A2PService.get_application_resource_from_spaces(self.spaces, arguments_dict["spearReferenceNumber"])
                    if (app_rs is None):
                        app_rs = self.create_application_resource_in_spaces(arguments_dict["spearReferenceNumber"], spear_app_id="", state=A2PService.APPSTATE_NOINFO)
                
                self.logger.log(logging.DEBUG, "Main: (%s)" % (request_uri_str))
                
                # User requests...
                #
                if (request_uri_str == 'a2puserreq:getapplication'):
                    app_rs = self.do_get_application(app_rs, request_uri, req_control_dict)
                elif (request_uri_str == 'a2puserreq:getapplicationattachment'):
                    app_rs = self.do_get_application_attachment(request_uri, req_control_dict)
                elif (request_uri_str == 'a2puserreq:getuserlist'):
                    self.do_get_user_list(request_uri, req_control_dict)
                elif (request_uri_str == 'a2puserreq:getuserlistfromspear'):
                    self.do_get_user_list_from_Spear(request_uri, req_control_dict)
                elif (request_uri_str == 'a2puserreq:getralist'):
                    self.do_get_ra_list(request_uri, req_control_dict)
                elif (request_uri_str == 'a2puserreq:getralistfromspear'):
                    self.do_get_ra_list_from_Spear(request_uri, req_control_dict)
                elif (request_uri_str == 'a2puserreq:acceptrejectapplication'):
                    self.do_send_acceptreject_application(request_uri, req_control_dict)
                elif (request_uri_str == 'a2puserreq:referapplication'):
                    self.do_send_refer_application(request_uri, req_control_dict)
                elif (request_uri_str == 'a2puserreq:authenticateapplicationattachment'):
                    self.do_send_authenticate_application_attachment(request_uri, req_control_dict)
                
                # Internally-generated control requests...
                #
                elif (request_uri_str == 'a2preq:getapplicationlist'):
                    self.do_get_application_list(request_uri, req_control_dict)
                elif (request_uri_str == 'a2preq:poll'):
                    self.do_poll(request_uri, req_control_dict)
                elif (request_uri_str == 'a2preq:startup'):
                    self.do_startup(request_uri, req_control_dict)
                
                # Internally-generated document requests...
                #
                elif (request_uri_str == 'a2preq:getresponsibleauthorityreferencenumber'):
                    app_rs = self.do_get_responsible_authority_reference_number(app_rs, request_uri, req_control_dict)
                elif (request_uri_str == 'a2preq:getreferralrequest'):
                    app_rs = self.do_get_referral_request(app_rs, request_uri, req_control_dict)
                elif (request_uri_str == 'a2preq:getattachmentmetadata'):
                    app_rs = self.do_get_attachment_metadata(app_rs, request_uri, req_control_dict)
                elif (request_uri_str == 'a2preq:getracomments'):
                    app_rs = self.do_get_ra_comments(app_rs, request_uri, req_control_dict)
                elif (request_uri_str == 'a2preq:relateraresponse'):
                    app_rs = self.do_relate_ra_response(app_rs, request_uri, req_control_dict)
                elif (request_uri_str == 'a2preq:getapplicationforplanningpermit'):
                    app_rs = self.do_get_application_for_planning_permit(app_rs, request_uri, req_control_dict)
                elif (request_uri_str == 'a2preq:getpdf'):
                    app_rs = self.do_get_attachment_pdf(app_rs, request_uri, req_control_dict)
                
                # save any application Resource changes. Must do this before sending
                # response to user (check_application_in_sync()).
                # 
                if (app_rs is not None):
                    A2PService.put_application_resource_to_spaces(self.spaces, arguments_dict["spearReferenceNumber"], app_rs)
                
                    # The application Resource still exists in Spaces so check it for 
                    # outstanding documents. If none then set state = INSYNC and 
                    # do_derivations()
                    #
                    self.check_application_in_sync(app_rs, req_control_dict)
                
            time.sleep(0.5)
    
    def check_application_in_sync(self, app_rs, req_control_dict):
        """
        Check that the argument application Resource 'app_rs' is synchronised
        with Spear. This is as simple as checking if there are any outstanding
        requests (to Spear) for attachments. If synchronised then write the 
        Resource to Spaces and tell the user.
        
        :param app_rs: Resource - Application to check.
        :param req_control_dict: Dictionary - User's request arguments.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        uuid_str = req_control_dict['a2p:request/uuid']
        response_uri = req_control_dict['a2p:response/uri']
        arguments_dict = req_control_dict["arguments_dict"]
        
#        self.logger.log(logging.DEBUG, "===================== Spear application (%s)" % app_rs.pretty_print())
        
        if (self.count_outstanding_attachments(app_rs) == 0):
            self.logger.log(logging.DEBUG, "===================== Spear application (%s) in sync" % arguments_dict["spearReferenceNumber"])
            
            # Derive the data required by eDAIS from the data we received from Spear.
            #
            self.do_derivations(app_rs)
            
#            import types
#            self.logger.log(logging.DEBUG, "===================== Spear application keys")
#            attachments_dict = app_rs.get_var("attachments")
#            for k in attachments_dict.iterkeys():
#                if (type(attachments_dict[k]) == types.DictType):
#                    for k2 in attachments_dict[k].iterkeys():
#                        if (type(attachments_dict[k][k2]) == types.DictType):
#                            for k3 in attachments_dict[k][k2].iterkeys():
#                                print "%s.%s.%s" % (k, k2, k3)
#            self.logger.log(logging.DEBUG, "===================== Spear application keys")
                
            # This application Resource is now in-sync with Spear.
            #
            A2PService.set_application_resource_state(app_rs, A2PService.APPSTATE_INSYNCWITHSPEAR)
            A2PService.set_application_resource_date_last_modified(app_rs)
            A2PService.put_application_resource_to_spaces(self.spaces, arguments_dict["spearReferenceNumber"], app_rs)
            
#            self.logger.log(logging.DEBUG, "===================== Spear application (%s)" % app_rs.pretty_print())
            
            # send response to user.
            #
            args_dict = {}
            args_dict["spearReferenceNumber"] = arguments_dict["spearReferenceNumber"]
            args_dict["spear_app_uri"] = A2PService.create_spear_application_uri(arguments_dict["spearReferenceNumber"])
            self.send_response_to_user(response_uri, Uri('a2puserreq:getapplication'), uuid_str, args_dict, error=None)
        
        else:
            self.logger.log(logging.DEBUG, "===================== Spear application (%s) NOT in sync" % arguments_dict["spearReferenceNumber"])
        
    def do_get_application_list(self, request_uri, req_control_dict):
        """
        This function returns details of the current applications stored in
        spaces.
        
        :param request_uri: Uri - User request Uri.
        :param req_control_dict: Dictionary - user's request arguments.
        """
        
        # get application list from Spear and store list, as separate application
        # Resources, in Spaces.
        #
        self.get_application_list_from_A2pSpear(req_control_dict)
        
    def do_get_application(self, app_rs, request_uri, req_control_dict):
        """
        This function retrieves from Spear the application with the reference
        number equal to arguments_dict["spearReferenceNumber"]. The A2pSpear 
        request is sent with the argument 'uuid_str' and the user response is 
        sent to the argument 'response_uri'.
        
        Note: 
            If 'response_uri' is passed (in 'req_control_dict') then this 
            request has come from a user, and we should respond. Otherwise 
            it has been generated internally and there's no reason to respond.
        
        :param app_rs: Resource - the application Resource
        :param request_uri: Uri - User request Uri.
        :param req_control_dict: Dictionary - user's request arguments.
        """
        
        uuid_str = req_control_dict['a2p:request/uuid']
        response_uri = req_control_dict['a2p:response/uri']
        arguments_dict = req_control_dict["arguments_dict"]
        spearReferenceNumber = arguments_dict["spearReferenceNumber"]
        
        self.logger.log(logging.DEBUG, "Retreiving application spearReferenceNumber (%s)" % spearReferenceNumber)
        
        state = A2PService.APPSTATE_NOINFO
        if (app_rs.has_var('control_dict') is True):
            control_dict = app_rs.get_var('control_dict')
            if ('state' in control_dict):
                state = control_dict['state']
        
        get_app = False
        
        if (state == A2PService.APPSTATE_NOINFO) or \
           (state == A2PService.APPSTATE_SUMMARYINFOONLY):
            get_app = True
            
        elif (state == A2PService.APPSTATE_UPDATINGFROMSPEAR):
            # we shouldn't see this state because we only ever process one
            # user request at a time. Its not possible for a Spear response
            # to be outstanding.
            #
            pass
        
        elif (state == A2PService.APPSTATE_EXPIRED):
            # TODO: What sets the 'expired' state of a Spear application Resource?
            #
             
            # delete the Resource ...
            #
            A2PService.delete_application_resource_from_spaces(self.spaces, spearReferenceNumber)
            
            # Create a new application Resource so we don't accidently use
            # any old data.
            #
            app_rs = self.create_application_resource_in_spaces(spearReferenceNumber, spear_app_id="", state=A2PService.APPSTATE_NOINFO)
            get_app = True
            
        elif (state == A2PService.APPSTATE_INSYNCWITHSPEAR):
            # return Resource to the user.
            #
            args_dict = {}
            args_dict["spearReferenceNumber"] = spearReferenceNumber
            args_dict["spear_app_uri"] = A2PService.create_spear_application_uri(arguments_dict["spearReferenceNumber"])
            self.send_response_to_user(response_uri, request_uri, uuid_str, args_dict, error=None)
            
        else:
            get_app = True
        
        if (get_app is True):
            # Tell user we're getting it!
            #
            A2PService.set_application_resource_state(app_rs, A2PService.APPSTATE_UPDATINGFROMSPEAR)
            
            # get application and attachments from A2pSpear
            #
            response_rs = self.get_application_from_A2pSpear(app_rs, req_control_dict)
            if (response_rs.get_var("success") is True):
                # save the application Resource to Spaces so users can see it.
                #
                self.get_application_attachments_from_A2pSpear(app_rs, req_control_dict)
            else:
                # Error getting Spear application. The application Resource is 
                # (probably) in a bad state. So let's delete it.
                #
                A2PService.delete_application_resource_from_spaces(self.spaces, spearReferenceNumber)
                app_rs = None
                
                # Let's augment the error string. In the case that the Spear 
                # reference number does not exist in Spear the result is a 
                # mismatched URL, not an error on the expected URL. As this 
                # error isn't very helpful we'll add our own.
                #
                error_string = response_rs.get_var("error")
                error_string += " Possibly bad Spear reference number."
                
                args_dict = {}
                args_dict["spearReferenceNumber"] = spearReferenceNumber
                self.send_response_to_user(response_uri, request_uri, uuid_str, args_dict, error=error_string)
        
        return app_rs
    
    def do_get_application_attachment(self, request_uri, req_control_dict):
        """
        This function returns a Resource to the user that contains the Uri of
        a Spear attachment related to the user's details.
        
        :param request_uri: Uri - User request Uri.
        :param req_control_dict: Dictionary - user's request arguments.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        uuid_str = req_control_dict['a2p:request/uuid']
        response_uri = req_control_dict['a2p:response/uri']
        arguments_dict = req_control_dict["arguments_dict"]
        documentId = arguments_dict["documentId"]
        
        attach_uri = A2PService.create_spear_application_attachment_uri(documentId)
        attach_rs = self.spaces.get(attach_uri)
        if (attach_rs is not None):
            args_dict = {}
            args_dict["documentId"] = documentId
            args_dict["spear_attach_uri"] = attach_uri
            self.send_response_to_user(response_uri, request_uri, uuid_str, args_dict, error=None)
        else:
            args_dict = {}
            error_string = "No Spear attachment found for document id (%s)" % (documentId)
            self.send_response_to_user(response_uri, request_uri, uuid_str, args_dict, error=error_string)
    
    def do_get_user_list(self, request_uri, req_control_dict):
        """
        This function retrieves the cached user list for the current Council. If
        not in Spaces then request it from Spear and return.
        
        :param request_uri: Uri - User request Uri.
        :param req_control_dict: Dictionary - user's request arguments.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        uuid_str = req_control_dict['a2p:request/uuid']
        response_uri = req_control_dict['a2p:response/uri']
        
        user_list_uri_list = self.spaces.find(A2PService.create_spear_user_list_uri())
        if (len(user_list_uri_list) > 0):
            args_dict = {}
            args_dict['a2p:user-list/uri'] = A2PService.create_spear_user_list_uri()
            self.send_response_to_user(response_uri, request_uri, uuid_str, args_dict, error=None)
        
        else:
            self.do_get_user_list_from_Spear(request_uri, req_control_dict)
    
    def do_get_user_list_from_Spear(self, request_uri, req_control_dict):
        """
        This function retrieves the Spear user list for the current Council.
        
        :param request_uri: Uri - User request Uri.
        :param req_control_dict: Dictionary - user's request arguments.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        uuid_str = req_control_dict['a2p:request/uuid']
        response_uri = req_control_dict['a2p:response/uri']
        
        response_rs = self.get_user_list_from_A2pSpear(req_control_dict)
        if (response_rs.get_var("success") is True):
            args_dict = {}
            args_dict['a2p:user-list/uri'] = A2PService.create_spear_user_list_uri()
            self.send_response_to_user(response_uri, request_uri, uuid_str, args_dict, error=None)
        
        else:
            args_dict = {}
            self.send_response_to_user(response_uri, request_uri, uuid_str, args_dict, error=response_rs.get_var("error"))
    
    def do_get_ra_list(self, request_uri, req_control_dict):
        """
        This function retrieves the cached RA list for the current Council. If
        not in Spaces then request it from Spear and return.
        
        :param request_uri: Uri - User request Uri.
        :param req_control_dict: Dictionary - user's request arguments.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        uuid_str = req_control_dict['a2p:request/uuid']
        response_uri = req_control_dict['a2p:response/uri']
        
        ra_list_uri_list = self.spaces.find(A2PService.create_spear_ra_list_uri())
        if (len(ra_list_uri_list) > 0):
            args_dict = {}
            args_dict['a2p:ra-list/uri'] = A2PService.create_spear_ra_list_uri()
            self.send_response_to_user(response_uri, request_uri, uuid_str, args_dict, error=None)
            
        else:
            self.do_get_ra_list_from_Spear(request_uri, req_control_dict)
    
    def do_get_ra_list_from_Spear(self, request_uri, req_control_dict):
        """
        This function retrieves the Spear RA list for the current Council.
        
        :param request_uri: Uri - User request Uri.
        :param req_control_dict: Dictionary - user's request arguments.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        uuid_str = req_control_dict['a2p:request/uuid']
        response_uri = req_control_dict['a2p:response/uri']
        
        response_rs = self.get_ra_list_from_A2pSpear(req_control_dict)
        if (response_rs.get_var("success") is True):
            args_dict = {}
            args_dict['a2p:ra-list/uri'] = A2PService.create_spear_ra_list_uri()
            self.send_response_to_user(response_uri, request_uri, uuid_str, args_dict, error=None)
        
        else:
            args_dict = {}
            self.send_response_to_user(response_uri, request_uri, uuid_str, args_dict, error=response_rs.get_var("error"))
    
    def do_send_acceptreject_application(self, request_uri, req_control_dict):
        """
        This function sends an Accept/Reject Application request to Spear.
        
        :param request_uri: Uri - User request Uri.
        :param req_control_dict: Dictionary - user's request arguments.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        # TODO: Send Accept/Reject Application request to Spear.
        #
        
        uuid_str = req_control_dict['a2p:request/uuid']
        response_uri = req_control_dict['a2p:response/uri']
        arguments_dict = req_control_dict["arguments_dict"]
        
#        arguments_dict['acceptreject_flag'] = acceptreject_flag
#        if (acceptreject_flag == A2PService.ACCEPTREJECT_REJECT):
#            arguments_dict['reject_reason'] = reject_reason
                
        response_rs = self.send_acceptreject_application_to_A2pSpear(req_control_dict)
        if (response_rs.get_var("success") is True):
            self.send_response_to_user(response_uri, request_uri, uuid_str, args_dict={}, error=None)
        else:
            self.send_response_to_user(response_uri, request_uri, uuid_str, args_dict={}, error=response_rs.get_var("error"))
        
        pass
    
    def do_send_refer_application(self, request_uri, req_control_dict):
        """
        This function sends an Application Referral request to Spear.
        
        :param request_uri: Uri - User request Uri.
        :param req_control_dict: Dictionary - user's request arguments.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        # TODO: Send Refer Application request to Spear.
        #
        
        uuid_str = req_control_dict['a2p:request/uuid']
        response_uri = req_control_dict['a2p:response/uri']
        arguments_dict = req_control_dict["arguments_dict"]
        
        pass
    
    def do_send_authenticate_application_attachment(self, request_uri, req_control_dict):
        """
        This function sends an Application Authenticate Attachment request to Spear.
        
        :param request_uri: Uri - User request Uri.
        :param req_control_dict: Dictionary - user's request arguments.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        # TODO: Send Authenticate Application Attachment request to Spear.
        #
        
        uuid_str = req_control_dict['a2p:request/uuid']
        response_uri = req_control_dict['a2p:response/uri']
        arguments_dict = req_control_dict["arguments_dict"]
        
        pass
    
    def do_poll(self, request_uri, req_control_dict):
        """
        This function retrieves the Council's current Spear activity log from Spear
        and processes.
        
        :param request_uri: Uri - User request Uri.
        :param req_control_dict: Dictionary - user's request arguments.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        # get activity log. Determine those Spear applications that are new or
        # have changed and retrieve.
        #
        response_rs = self.get_activity_log_from_A2pSpear(req_control_dict)
        if (response_rs.get_var("success") is True):
            spear_reference_numbers_dict = {}
            
            self.logger.log(NodeLogger.TRACE, "activity log (%s)" % response_rs.pretty_print())
            
            # Parse returned XML.
            #
            # Note: We use an HTML parser rather than an XML parser for convenience
            #       as we need a simple SAX parser and python's HTMLParser is just that.
            #
            try:
                self.parser = SpearActivityLogParser(spear_reference_numbers_dict)
                self.parser.feed(response_rs.get_var("xml"))
                self.parser.close()
            except HTMLParseError as e:
                self.logger.log(logging.ERROR, "XML Parsing Error (%s)" % e)
            except:
                self.logger.log(logging.ERROR, "XML Parsing Error")
                
            self.logger.log(NodeLogger.TRACE, "activity log spear_reference_numbers_dict (%s)" % spear_reference_numbers_dict)
            
            for spearReferenceNumber in spear_reference_numbers_dict.keys():
                self.logger.log(logging.DEBUG, "Retrieving Spear application with ref # from Spaces (%s) ...." % spearReferenceNumber)
                
                arguments_dict = {}
                arguments_dict["spearReferenceNumber"] = spearReferenceNumber
                
                # Allocate a UUID that we'll use to tie requests with responses. 
                #
                uuid_str = str(uuid.uuid4())
    
                # Create a new Resource that contains the A2PSpear request and response Uri, and send to A2pProcessThread.
                #
                a2pspear_rs = self.create_queue_request_resource('a2puserreq:getapplication', uuid_str, arguments_dict, response_uri=None)
                self.add_request_to_internal_queue(a2pspear_rs)
        
        # Note:
        #
        # The following requests are all put on the external queue so we don't
        # interleave requests with those for applications and attachments above.
        #
        
        # get current application list.
        #
        request_rs = self.create_queue_request_resource('a2preq:getapplicationlist', str(uuid.uuid4()), arguments_dict={}, response_uri=None)
        self.add_request_to_external_queue(request_rs)
        
        # get list of users from Spear and store list in Spaces.
        #
        request_rs = self.create_queue_request_resource('a2puserreq:getuserlistfromspear', str(uuid.uuid4()), arguments_dict={}, response_uri=None)
        self.add_request_to_external_queue(request_rs)
        
        # get list of RAs from Spear and store list in Spaces.
        #
        request_rs = self.create_queue_request_resource('a2puserreq:getralistfromspear', str(uuid.uuid4()), arguments_dict={}, response_uri=None)
        self.add_request_to_external_queue(request_rs)
        
    def do_startup(self, request_uri, req_control_dict):
        """
        This function retrieves the current list of Spear applications from
        Spear. 
        
        :param request_uri: Uri - User request Uri.
        :param req_control_dict: Dictionary - user's request arguments.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        # We could put a request on a queue but, for startup only, we need to
        # go and get the applications in the application list. So we must do 
        # that here.
        #
        
        # get application list from Spear and store list, as separate application
        # Resources, in Spaces.
        #
        response_rs = self.get_application_list_from_A2pSpear(req_control_dict)
        if (response_rs.get_var("success") is True):
                        
            # get all current applications by sending requests to our external queue.
            #
            # Note:
            #
            # We must do this because the retrieval of an application involves
            # many internal queue requests. If we directly request many apps
            # (from Spear) here this will result in interleaved internal queue
            # requests for different apps, which will fail because Spear does not
            # allow this.
            #
            if (response_rs.has_var("sid002b_dict_list") is True):
                sid002b_dict_list = response_rs.get_var("sid002b_dict_list")
                if (len(sid002b_dict_list) > 0):
                    for d in sid002b_dict_list:
                        arguments_dict = {}
                        arguments_dict["spearReferenceNumber"] = d["sid002b_spear_ref"]
                        
                        # Allocate a UUID that we'll use to tie requests with responses. 
                        #
                        uuid_str = str(uuid.uuid4())
            
                        # Create a new Resource that contains the A2PSpear request and response Uri, and send to A2pProcessThread.
                        #
                        request_rs = self.create_queue_request_resource('a2puserreq:getapplication', uuid_str, arguments_dict, response_uri=None)
                        self.add_request_to_external_queue(request_rs)
        
        # get list of users from Spear and store list in Spaces.
        #
        request_rs = self.create_queue_request_resource('a2puserreq:getuserlistfromspear', str(uuid.uuid4()), arguments_dict={}, response_uri=None)
        self.add_request_to_external_queue(request_rs)
        
        # get list of RAs from Spear and store list in Spaces.
        #
        request_rs = self.create_queue_request_resource('a2puserreq:getralistfromspear', str(uuid.uuid4()), arguments_dict={}, response_uri=None)
        self.add_request_to_external_queue(request_rs)
        
    def do_get_responsible_authority_reference_number(self, app_rs, request_uri, req_control_dict):
        """
        This function retrieves a Spear "Responsible Authority Reference Number"
        (sid072a) document.
        
        :param app_rs: Resource - the application Resource
        :param request_uri: Uri - User request Uri.
        :param req_control_dict: Dictionary - user's request arguments.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        uuid_str = req_control_dict['a2p:request/uuid']
        arguments_dict = req_control_dict["arguments_dict"]
        spearReferenceNumber = arguments_dict["spearReferenceNumber"]
        attachment_url = arguments_dict["documentUrl"] 
        
        # Request sid072a and merge the attachment data with the application data.
        #
        response_rs = self.send_applicationdocument_responsibleauthorityreferencenumber_request_to_A2pSpear(uuid_str, attachment_url, spearReferenceNumber)
        
        if (response_rs.get_var("success") is True) and (app_rs is not None):
            app_rs.set_var("sid072a_dict", response_rs.get_var("sid072a_dict"))
            
            # Remove document URL from list of outstanding URLs.
            #
            self.remove_outstanding_attachment(app_rs, attachment_url, spearReferenceNumber)
        
        return app_rs
    
    def do_get_referral_request(self, app_rs, request_uri, req_control_dict):
        """
        This function retrieves a Spear "Referral Request" (sid061a) document.
        
        :param app_rs: Resource - the application Resource
        :param request_uri: Uri - User request Uri.
        :param req_control_dict: Dictionary - user's request arguments.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        uuid_str = req_control_dict['a2p:request/uuid']
        arguments_dict = req_control_dict["arguments_dict"]
        spearReferenceNumber = arguments_dict["spearReferenceNumber"]
        attachment_url = arguments_dict["documentUrl"] 
        attachment_document_id = arguments_dict["documentId"]
        
        # Request sid061a and merge the attachment data with other referral-request data.
        #
        response_rs = self.send_applicationdocument_referralrequest_request_to_A2pSpear(uuid_str, attachment_url, spearReferenceNumber)
        
        if (response_rs.get_var("success") is True) and (app_rs is not None):
            sid061a_dict_dict = {}
            if (app_rs.has_var("sid061a_dict_dict") is True):
                sid061a_dict_dict = app_rs.get_var("sid061a_dict_dict")
            sid061a_dict_dict[attachment_document_id] = response_rs.get_var("sid061a_dict")
            app_rs.set_var("sid061a_dict_dict", sid061a_dict_dict)
            
            # Remove document URL from list of outstanding URLs.
            #
            self.remove_outstanding_attachment(app_rs, attachment_url, spearReferenceNumber)
        
        return app_rs
    
    def do_get_attachment_metadata(self, app_rs, request_uri, req_control_dict):
        """
        This function retrieves a Spear Application Document (sid005a) document.
        Based on the result metadata we may need to retrieve more Spear documents.
        
        :param app_rs: Resource - the application Resource
        :param request_uri: Uri - User request Uri.
        :param req_control_dict: Dictionary - user's request arguments.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        uuid_str = req_control_dict['a2p:request/uuid']
        response_uri = req_control_dict['a2p:response/uri']
        arguments_dict = req_control_dict["arguments_dict"]
        spearReferenceNumber = arguments_dict["spearReferenceNumber"]
        attachment_url = arguments_dict["documentUrl"] 
        attachment_document_id = arguments_dict["documentId"]
        
        # Request sid005a and merge the attachment meta-data with other attachment meta-data.
        #
        response_rs = self.send_applicationdocument_request_to_A2pSpear(uuid_str, attachment_url, spearReferenceNumber)
        
        if (response_rs.get_var("success") is True) and (app_rs is not None):
            sid005a_dict_dict = {}
            if (app_rs.has_var("sid005a_dict_dict") is True):
                sid005a_dict_dict = app_rs.get_var("sid005a_dict_dict")
            sid005a_dict_dict[attachment_document_id] = response_rs.get_var("sid005a_dict")
            app_rs.set_var("sid005a_dict_dict", sid005a_dict_dict)
            
            # Remove document URL from list of outstanding URLs.
            #
            self.remove_outstanding_attachment(app_rs, attachment_url, spearReferenceNumber)
            
            # Possibilities from this point are:
            # - PDF
            # - sid006a
            # - None
            #
                    
            if (response_rs.has_var("sid005a_dict")):
                sid005a_dict = response_rs.get_var("sid005a_dict")
                self.logger.log(NodeLogger.TRACE, "--------------------- sid005a_dict (%s)" % sid005a_dict)
                        
                if (sid005a_dict.has_key('sid005a_current_document_doctype') and (sid005a_dict["sid005a_current_document_doctype"].startswith("Application for") or sid005a_dict["sid005a_current_document_doctype"] == 'Certification')):
                    args_dict = {}
                    args_dict["spearReferenceNumber"] = spearReferenceNumber
                    args_dict["documentUrl"] = sid005a_dict["document_url"]
                    args_dict["documentId"] = attachment_document_id
                    queue_rs = self.create_queue_request_resource("a2preq:getapplicationforplanningpermit", uuid_str, args_dict, response_uri)
                    self.add_request_to_internal_queue(queue_rs)
                    
                    # Add document URL to list of outstanding URLs.
                    #
                    self.add_outstanding_attachment(app_rs, sid005a_dict["document_url"], spearReferenceNumber)
                
                # Not all sid005a documents have attachments.
                #
                elif ("document_url" in sid005a_dict):
                    if (len(sid005a_dict["document_url"]) == 0):
                        self.logger.log(NodeLogger.TRACE, "===================== Ignoring (%s)" % (sid005a_dict["sid005a_current_document_doctype"]))
                    
                    else:
                        args_dict = {}
                        args_dict["spearReferenceNumber"] = spearReferenceNumber
                        args_dict["documentUrl"] = sid005a_dict["document_url"]
                        args_dict["documentId"] = attachment_document_id
                        queue_rs = self.create_queue_request_resource("a2preq:getpdf", uuid_str, args_dict, response_uri)
                        self.add_request_to_internal_queue(queue_rs)
                        
                        # Add document URL to list of outstanding URLs.
                        #
                        self.add_outstanding_attachment(app_rs, sid005a_dict["document_url"], spearReferenceNumber)
        
        return app_rs
    
    def do_get_ra_comments(self, app_rs, request_uri, req_control_dict):
        """
        This function retrieves a Spear "RA Comments" (sid075c) document.
        
        :param app_rs: Resource - the application Resource
        :param request_uri: Uri - User request Uri.
        :param req_control_dict: Dictionary - user's request arguments.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        uuid_str = req_control_dict['a2p:request/uuid']
        arguments_dict = req_control_dict["arguments_dict"]
        spearReferenceNumber = arguments_dict["spearReferenceNumber"]
        complementary_url = arguments_dict["documentUrl"] 
        attachment_document_id = arguments_dict["documentId"]
        
        # Request sid075c and merge the attachment data with other RA-comments data.
        #
        response_rs = self.send_applicationdocument_racomments_request_to_A2pSpear(uuid_str, complementary_url, spearReferenceNumber)
        
        if (response_rs.get_var("success") is True) and (app_rs is not None):
            sid075c_dict_dict = {}
            if (app_rs.has_var("sid075c_dict_dict") is True):
                sid075c_dict_dict = app_rs.get_var("sid075c_dict_dict")
            sid075c_dict_dict[attachment_document_id] = response_rs.get_var("sid075c_dict")
            app_rs.set_var("sid075c_dict_dict", sid075c_dict_dict)
            
            # Remove document URL from list of outstanding URLs.
            #
            self.remove_outstanding_attachment(app_rs, complementary_url, spearReferenceNumber)
        
        return app_rs
    
    def do_relate_ra_response(self, app_rs, request_uri, req_control_dict):
        """
        This function relates a Spear "RA Response" document to a Spear "RA
        Request" document.
        
        :param app_rs: Resource - the application Resource
        :param request_uri: Uri - User request Uri.
        :param req_control_dict: Dictionary - user's request arguments.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        arguments_dict = req_control_dict["arguments_dict"]
        spearReferenceNumber = arguments_dict["spearReferenceNumber"]
        complementary_url = arguments_dict["documentUrl"] 
        attachment_document_id = arguments_dict["documentId"]
        
        if (app_rs is not None):
            if (app_rs.has_var("sid005a_dict_dict") is True):
                sid005a_dict_dict = app_rs.get_var("sid005a_dict_dict")
                if (attachment_document_id in sid005a_dict_dict):
                    sid005a_dict = sid005a_dict_dict[attachment_document_id]
                    
                    # Remember: sid005a_dict_dict may contain several referral requests.
                    #
                    complementary_url_parts = urlparse.urlparse(str(complementary_url))
                    if ("sid005a_ra_request_document_id_list" not in sid005a_dict):
                        sid005a_dict["sid005a_ra_request_document_id_list"] = []
                    sid005a_dict["sid005a_ra_request_document_id_list"].append(self.get_urlparameter("documentId", complementary_url_parts.query))
        
        return app_rs
    
    def do_get_application_for_planning_permit(self, app_rs, request_uri, req_control_dict):
        """
        This function retrieves a Spear "Application for ..." (sid006a) document.
        This includes:
        
        - Application for Planning Permit
        - Application for Certification
        - Application for Planning Permit and Certification
        
        :param app_rs: Resource - the application Resource
        :param request_uri: Uri - User request Uri.
        :param req_control_dict: Dictionary - user's request arguments.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        uuid_str = req_control_dict['a2p:request/uuid']
        arguments_dict = req_control_dict["arguments_dict"]
        spearReferenceNumber = arguments_dict["spearReferenceNumber"]
        attachment_url = arguments_dict["documentUrl"] 
        
        # Request sid075c and merge the attachment data with other RA-comments data.
        #
        response_rs = self.send_applicationdocument_applicationforplanningpermit_request_to_A2pSpear(uuid_str, attachment_url, spearReferenceNumber)
        
        if (response_rs.get_var("success") is True) and (app_rs is not None):
            app_rs.set_var("sid006a_dict", response_rs.get_var("sid006a_dict"))
            
            # Remove document URL from list of outstanding URLs.
            #
            self.remove_outstanding_attachment(app_rs, attachment_url, spearReferenceNumber)
        
        return app_rs
    
    def do_get_attachment_pdf(self, app_rs, request_uri, req_control_dict):
        """
        This function retrieves a Spear PDF document.
        
        :param app_rs: Resource - the application Resource
        :param request_uri: Uri - User request Uri.
        :param req_control_dict: Dictionary - user's request arguments.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        uuid_str = req_control_dict['a2p:request/uuid']
        arguments_dict = req_control_dict["arguments_dict"]
        spearReferenceNumber = arguments_dict["spearReferenceNumber"]
        attachment_url = arguments_dict["documentUrl"] 
        
        # Request sid075c and merge the attachment data with other RA-comments data.
        #
        response_rs = self.send_applicationdocument_pdf_request_to_A2pSpear(uuid_str, attachment_url, spearReferenceNumber)
        
        if (response_rs.get_var("success") is True) and (app_rs is not None):
            # create the attachment Resource after deleting any already existing.
            #
            url_parts = urlparse.urlparse(str(attachment_url))
            doc_id = self.get_urlparameter("documentId", url_parts.query)
            self.create_application_attachment_resource_in_spaces(spearReferenceNumber, doc_id, response_rs.get_var("pdf"))
            
            # Remove document URL from list of outstanding URLs.
            #
            self.remove_outstanding_attachment(app_rs, attachment_url, spearReferenceNumber)
        
        return app_rs
    
    def add_outstanding_attachment(self, app_rs, attachment_url, spearReferenceNumber):
        """
        Add the 'attachment_url' argument to the argument application
        Resource's "outstanding_attachments_list" list.
        
        :param app_rs: Resource - the application Resource
        :param attachment_url: String
        """
        
        outstanding_attachments_list = app_rs.get_var("outstanding_attachments_list")
        outstanding_attachments_list.append(attachment_url)
        app_rs.set_var("outstanding_attachments_list", outstanding_attachments_list)
        
    def remove_outstanding_attachment(self, app_rs, attachment_url, spearReferenceNumber):
        """
        Remove the 'attachment_url' argument from the argument application
        Resource's "outstanding_attachments_list" list.
        
        :param app_rs: Resource - the application Resource
        :param attachment_url: String
        """
        
        outstanding_attachments_list = app_rs.get_var("outstanding_attachments_list")
        for i in range(0, len(outstanding_attachments_list)):
            if (outstanding_attachments_list[i] == attachment_url):
                del outstanding_attachments_list[i]
                app_rs.set_var("outstanding_attachments_list", outstanding_attachments_list)
                break
        
    def count_outstanding_attachments(self, app_rs):
        """
        Return the number of elements in the argument application Resource's
        "outstanding_attachments_list" list.
        
        :param app_rs: Resource - the application Resource
        :rtype: int 
        """
        
        return len(app_rs.get_var("outstanding_attachments_list"))
    
    def get_application_list_from_A2pSpear(self, req_control_dict):
        """
        This function requests, from A2pSpear, the application list. This function
        is called during polling so its likely that retrieved applications will
        already exist. If the application does not already exist then we create
        it, otherwise we do nothing - any modifications to an application will 
        have been retrieved during polling of the activity log.
        
        :param req_control_dict: Dictionary - user's request arguments.
        :rtype: Resource - The response from A2pSpear.
        """
        
        uuid_str = req_control_dict['a2p:request/uuid']
        
        # get list of applications from Spear.
        #
        response_rs = self.send_application_list_request_to_A2pSpear(uuid_str)
        if (response_rs.get_var("success") is True):
            # From the list of applications retrieved create application Resources.
            #
            # Note: The 'SpearReferenceNumber' is the same as 'sid002b_spear_ref'.
            #       The 'SpearAppToken' is the same as 'sid002b_app_id'.      
            #
            sid002b_dict_list = response_rs.get_var("sid002b_dict_list")
            if (len(sid002b_dict_list) > 0):
                for d in sid002b_dict_list:
                    # If the user requests...
                    #
                    # - the application list
                    #   - do_get_application_list() will fill in the correct summary
                    #     fields from 'sid002b_dict' for each application in the list.
                    # - an application
                    #   - we will go and get the rest of the application data (incl. 
                    #     attachments), if we haven't already based on polling log 
                    #     data.
                    #
                    
                    # Let's add (or update) a flag in each application Resource to 
                    # tell us that the application is 'current'. We know this is True 
                    # because we've just retrieved the current list of apps. Sadly
                    # there is nothing in the Spear application that tells us an
                    # app. is 'current'.
                    #
                    
                    app_rs_uri = A2PService.create_spear_application_uri(d["sid002b_spear_ref"])
                    app_rs = self.spaces.get(app_rs_uri)
                    if (app_rs is None):
                        self.create_application_resource_in_spaces(d["sid002b_spear_ref"], d["sid002b_app_id"], A2PService.APPSTATE_SUMMARYINFOONLY, is_current=True, sid002b_dict=d)
                    else:
                        # TODO: Should we update our app_rs with sid002b data?
                        #
                        A2PService.set_application_resource_current(app_rs, is_current=True)
                        A2PService.put_application_resource_to_spaces(self.spaces, d["sid002b_spear_ref"], app_rs)
                        
        return response_rs
    
    def get_application_from_A2pSpear(self, app_rs, req_control_dict):
        """
        This function requests, from A2pSpear, the application with a Spear
        application reference number matching the argument reference number. 
        The returned application data is then merged with the argument
        application Resource.
        
        :param app_rs: Resource - the application Resource
        :param req_control_dict: Dictionary - user's request arguments.
        :rtype: Resource - The response from A2pSpear.
        """
        
        uuid_str = req_control_dict['a2p:request/uuid']
        arguments_dict = req_control_dict["arguments_dict"]
        spearReferenceNumber = arguments_dict["spearReferenceNumber"]
        
        response_rs = self.send_application_request_to_A2pSpear(uuid_str, spearReferenceNumber)
        if (response_rs.get_var("success") is True) and (app_rs is not None):
            self.logger.log(NodeLogger.TRACE, "===================== sid004 (%s)" % (response_rs.pretty_print()))
        
            # merge the application data with the summary data.
            #
            sid004_dict = response_rs.get_var("sid004_dict")
            for sid004_attachment_dict in sid004_dict["sid004_attachment_dict_list"]:
                attachment_url = sid004_attachment_dict["document_url"]
                attachment_url_parts = urlparse.urlparse(str(attachment_url)) 
                sid004_attachment_dict["sid004_doc_id"] = self.get_urlparameter("documentId", attachment_url_parts.query)
            app_rs.set_var("sid004_dict", sid004_dict)
        
        return response_rs
    
    def get_application_attachments_from_A2pSpear(self, app_rs, req_control_dict):
        """
        This function requests, from A2pSpear, the application attachments for
        the argument Spear application reference number. The returned application 
        attachment meta-data is then merged with the argument application Resource. 
        The returned attachments are stored in their own Resources and saved to Spaces. 
        
        Note: 
            From the "Application Details" (sid004) page the documents clickthru as follows::
        
                Applicant Contact:
                  - "Cultural Heritage Management Plan requirements"
                    > None
                  - "Application for ..." (Planning Permit, Certification, Planning Permit and Certification)
                    > sid005a
                      > sid006a
                  - "Planning Property Report"
                    > services.land.vic.gov.au (ignored)
                  - Others ("Copy of Title", "Plan of Elevations & Sections", etc)
                    > sid005a
                      > PDF
            
                Responsible Authority:
                  - Application Acceptance / Rejection
                    > None
                  - Referral Request
                    > sid061a
                  - Responsible Authority Reference Number
                    > sid072a
                  - Others (with document_url)
                    > sid005a
                      > PDF
            
                Referral Authorities:
                  - RA Response
                    > sid005a
                  - RA Comments
                    > sid075c
                
                Online Objectors:
                  - Objection
                    > sid005a
            
                VCAT:
        
        :param app_rs: Resource - the application Resource
        :param req_control_dict: Dictionary - user's request arguments.
        """
        
        uuid_str = req_control_dict['a2p:request/uuid']
        response_uri = req_control_dict['a2p:response/uri']
        arguments_dict = req_control_dict["arguments_dict"]
        spearReferenceNumber = arguments_dict["spearReferenceNumber"]
        
        sid004_dict = app_rs.get_var("sid004_dict")
        
        for sid004_attachment_dict in sid004_dict["sid004_attachment_dict_list"]:
            attachment_document_id = sid004_attachment_dict["sid004_doc_id"]
            
            if ("sid004_doctype" in sid004_attachment_dict) and ("document_url" in sid004_attachment_dict):
                # get the attachment's document id...
                #
                attachment_url = sid004_attachment_dict["document_url"]
                
                if (sid004_attachment_dict["sid004_doctype"] == "Responsible Authority Reference Number"):
                    args_dict = {}
                    args_dict["spearReferenceNumber"] = spearReferenceNumber
                    args_dict["documentUrl"] = attachment_url
                    queue_rs = self.create_queue_request_resource("a2preq:getresponsibleauthorityreferencenumber", uuid_str, args_dict, response_uri)
                    self.add_request_to_internal_queue(queue_rs)
                    
                    # Add document URL to list of outstanding URLs.
                    #
                    self.add_outstanding_attachment(app_rs, attachment_url, spearReferenceNumber)
                    
                elif (sid004_attachment_dict["sid004_doctype"] == "Referral Request"):
                    args_dict = {}
                    args_dict["spearReferenceNumber"] = spearReferenceNumber
                    args_dict["documentUrl"] = attachment_url
                    args_dict["documentId"] = attachment_document_id
                    queue_rs = self.create_queue_request_resource("a2preq:getreferralrequest", uuid_str, args_dict, response_uri)
                    self.add_request_to_internal_queue(queue_rs)
                    
                    # Add document URL to list of outstanding URLs.
                    #
                    self.add_outstanding_attachment(app_rs, attachment_url, spearReferenceNumber)
                    
                elif (sid004_attachment_dict["sid004_doctype"] == "RA Response"):
                    args_dict = {}
                    args_dict["spearReferenceNumber"] = spearReferenceNumber
                    args_dict["documentUrl"] = attachment_url
                    args_dict["documentId"] = attachment_document_id
                    queue_rs = self.create_queue_request_resource("a2preq:getattachmentmetadata", uuid_str, args_dict, response_uri)
                    self.add_request_to_internal_queue(queue_rs)
                    
                    # Add document URL to list of outstanding URLs.
                    #
                    self.add_outstanding_attachment(app_rs, attachment_url, spearReferenceNumber)
                    
                    if (len(sid004_attachment_dict["sid004_complementary_doctype_link_dict_list"]) > 0):
                        for sid004_complementary_doctype_link_dict in sid004_attachment_dict["sid004_complementary_doctype_link_dict_list"]:
                            self.logger.log(NodeLogger.TRACE, "===================== Complementary document (%s)(%s)" % (sid004_complementary_doctype_link_dict["title"], sid004_complementary_doctype_link_dict["url"]))
                            
                            complementary_url = sid004_complementary_doctype_link_dict["url"]
                            
                            if (sid004_complementary_doctype_link_dict["title"] == "(View RA Comments)"):
                                args_dict = {}
                                args_dict["spearReferenceNumber"] = spearReferenceNumber
                                args_dict["documentUrl"] = complementary_url
                                args_dict["documentId"] = attachment_document_id
                                queue_rs = self.create_queue_request_resource("a2preq:getracomments", uuid_str, args_dict, response_uri)
                                self.add_request_to_internal_queue(queue_rs)
                                
                                # Add document URL to list of outstanding URLs.
                                #
                                self.add_outstanding_attachment(app_rs, complementary_url, spearReferenceNumber)
                                
                            elif (sid004_complementary_doctype_link_dict["title"].startswith("(View Referral") is True):
                                args_dict = {}
                                args_dict["spearReferenceNumber"] = spearReferenceNumber
                                args_dict["documentUrl"] = complementary_url
                                args_dict["documentId"] = attachment_document_id
                                queue_rs = self.create_queue_request_resource("a2preq:relateraresponse", uuid_str, args_dict, response_uri)
                                self.add_request_to_internal_queue(queue_rs)
                                
                elif (sid004_attachment_dict["sid004_doctype"] == "Assess Proposed Changes to Application"):
                    self.logger.log(NodeLogger.TRACE, "===================== Ignoring (%s)" % (sid004_attachment_dict["sid004_doctype"]))
                    
                    # Request sid046b.
                    #
                
                elif (sid004_attachment_dict["sid004_doctype"] == "Planning Property Report"):
                    # Non-Spear request.
                    #
                    self.logger.log(NodeLogger.TRACE, "===================== Non-Spear document (%s). Ignoring." % (sid004_attachment_dict["sid004_doctype"]))
                
                elif (len(sid004_attachment_dict["document_url"]) == 0):
                    self.logger.log(NodeLogger.TRACE, "===================== Ignoring (%s)" % (sid004_attachment_dict["sid004_doctype"]))
                
                else:
                    args_dict = {}
                    args_dict["spearReferenceNumber"] = spearReferenceNumber
                    args_dict["documentUrl"] = attachment_url
                    args_dict["documentId"] = attachment_document_id
                    queue_rs = self.create_queue_request_resource("a2preq:getattachmentmetadata", uuid_str, args_dict, response_uri)
                    self.add_request_to_internal_queue(queue_rs)
                    
                    # Add document URL to list of outstanding URLs.
                    #
                    self.add_outstanding_attachment(app_rs, attachment_url, spearReferenceNumber)
    
    def get_user_list_from_A2pSpear(self, req_control_dict):
        """
        This function requests, from A2pSpear, the list of council users in Spear.
        
        :param req_control_dict: Dictionary - user's request arguments.
        :rtype: Resource - The response from A2pSpear.
        """
        
        uuid_str = req_control_dict['a2p:request/uuid']

        response_rs = self.send_user_list_request_to_A2pSpear(uuid_str)
        if (response_rs.get_var("success") is True):
            self.create_user_list_resource_in_spaces(response_rs.get_var("sid102a_dict_list"))
        
        return response_rs
    
    def get_ra_list_from_A2pSpear(self, req_control_dict):
        """
        This function requests, from A2pSpear, the list of council RAs in Spear.
        
        :param req_control_dict: Dictionary - user's request arguments.
        :rtype: Resource - The response from A2pSpear.
        """
        
        uuid_str = req_control_dict['a2p:request/uuid']

        response_rs = self.send_ra_list_request_to_A2pSpear(uuid_str)
        if (response_rs.get_var("success") is True):
            self.create_ra_list_resource_in_spaces(response_rs.get_var("sid112a_dict_list"))
        
        return response_rs
    
    def get_activity_log_from_A2pSpear(self, req_control_dict):
        """
        This function requests, from A2pSpear, the council's activity log.
        
        :param req_control_dict: Dictionary - user's request arguments.
        :rtype: Resource - The response from A2pSpear.
        """
        
        return self.send_activity_log_request_to_A2pSpear(req_control_dict)
    
    def send_application_list_request_to_A2pSpear(self, uuid_str):
        """
        Create the request Resource - "Application Details" (sid004), send to 
        Spear and return the result as a Resource.
        
        :param uuid_str: String - UUID of user request.
        :rtype: Resource - Response from A2pSpear
        """
        
        self.logger.log(NodeLogger.TRACE, "===================== Requesting application list (sid002b)")
        
        # get list of applications from Spear.
        #
        args_dict = {}
        rs = self.send_request_to_A2pSpear("spear:request/currentapplicationlist", uuid_str, args_dict)
        
        self.logger.log(NodeLogger.TRACE, "===================== sid002b (%s)" % (rs.pretty_print()))
        
        return rs
    
    def send_application_request_to_A2pSpear(self, uuid_str, spear_reference_number):
        """
        Create the request Resource - "Application Details" (sid004), send to 
        Spear and return the result as a Resource.
        
        :param uuid_str: String - UUID of user request.
        :param spear_reference_number: String - the Spear application's reference number
        :rtype: Resource - Response from A2pSpear
        """
        
        self.logger.log(NodeLogger.TRACE, "===================== Requesting application (sid004)")
        
        # create the request Resource - "Application Details" (sid004)
        # - this depends on "Application List"
        #
        args_dict = {}
        args_dict["spearReferenceNumber"] = spear_reference_number
        rs = self.send_request_to_A2pSpear("spear:request/applicationdetails", uuid_str, args_dict)
        
        self.logger.log(NodeLogger.TRACE, "===================== sid102a (%s)" % (rs.pretty_print()))
        
        return rs
    
    def send_activity_log_request_to_A2pSpear(self, req_control_dict):
        """
        Create the request Resource - "Activity Log", send to Spear and return
        the result as a Resource.
        
        :param uuid_str: String - UUID of user request.
        :rtype: Resource - Response from A2pSpear
        """
        
        self.logger.log(NodeLogger.TRACE, "===================== Requesting activity log")
        
        uuid_str = req_control_dict['a2p:request/uuid']
        arguments_dict = req_control_dict["arguments_dict"]
        
        rs = self.send_request_to_A2pSpear("spear:request/activitylog", uuid_str, arguments_dict)
        
        self.logger.log(NodeLogger.TRACE, "===================== sid102a (%s)" % (rs.pretty_print()))
        
        return rs
    
    def send_user_list_request_to_A2pSpear(self, uuid_str):
        """
        Create the request Resource - "User List" (sid102a), send to Spear and 
        return the result as a Resource.
        
        :param uuid_str: String - UUID of user request.
        :rtype: Resource - Response from A2pSpear
        """
        
        self.logger.log(NodeLogger.TRACE, "===================== Requesting user list sid102a")
        
        args_dict = {}
        
        rs = self.send_request_to_A2pSpear("spear:request/users", uuid_str, args_dict)
        
        self.logger.log(NodeLogger.TRACE, "===================== sid102a (%s)" % (rs.pretty_print()))
        
        return rs
    
    def send_ra_list_request_to_A2pSpear(self, uuid_str):
        """
        Create the request Resource - "RA List" (sid112a), send to Spear and 
        return the result as a Resource.
        
        :param uuid_str: String - UUID of user request.
        :rtype: Resource - Response from A2pSpear
        """
        
        self.logger.log(NodeLogger.TRACE, "===================== Requesting RA list sid112a")
        
        args_dict = {}
        rs = self.send_request_to_A2pSpear("spear:request/ras", uuid_str, args_dict)
        
        self.logger.log(NodeLogger.TRACE, "===================== sid112a (%s)" % (rs.pretty_print()))
        
        return rs
    
    def send_acceptreject_application_to_A2pSpear(self, req_control_dict):
        """
        """
        
        uuid_str = req_control_dict['a2p:request/uuid']
        arguments_dict = req_control_dict["arguments_dict"]
        
        if (arguments_dict['acceptreject_flag'] == A2PServiceAPI.ACCEPTREJECT_ACCEPT):
            self.logger.log(NodeLogger.TRACE, "===================== Sending accept/reject (ACCEPT), spearReferenceNumber (%s)" % (arguments_dict["spearReferenceNumber"]))
            pass
        elif (arguments_dict['acceptreject_flag'] == A2PServiceAPI.ACCEPTREJECT_ACCEPTWITHCHANGE):
            self.logger.log(NodeLogger.TRACE, "===================== Sending accept/reject (ACCEPT WITH CHANGE), spearReferenceNumber (%s)" % (arguments_dict["spearReferenceNumber"]))
            pass
        elif (arguments_dict['acceptreject_flag'] == A2PServiceAPI.ACCEPTREJECT_REJECT):
            self.logger.log(NodeLogger.TRACE, "===================== Sending accept/reject (REJECT), spearReferenceNumber (%s)" % (arguments_dict["spearReferenceNumber"]))
            
            rs = self.send_request_to_A2pSpear("spear:request/send-acceptreject-reject", uuid_str, arguments_dict)
        
        self.logger.log(NodeLogger.TRACE, "===================== sid072a (%s)" % (rs.pretty_print()))
        
        return rs
    
    def send_applicationdocument_responsibleauthorityreferencenumber_request_to_A2pSpear(self, uuid_str, url, spear_reference_number):
        """
        Create the request Resource - "Application Document (Responsible Authority Reference Number)" (sid072a),
        send to Spear and return the result as a Resource.
        
        :param uuid_str: String - UUID of user request.
        :param url: String - URL of Spear application document
        :param spear_reference_number: String - the Spear application's reference number
        :rtype: Resource - Response from A2pSpear
        """
        
        self.logger.log(NodeLogger.TRACE, "===================== Requesting sid072a (%s)" % (url))
        
        url_parts = urlparse.urlparse(str(url))
        
        args_dict = {}
        args_dict["documentUrlPath"] = url_parts.path
        args_dict["documentUrlQuery"] = url_parts.query
        args_dict["spearReferenceNumber"] = spear_reference_number
        rs = self.send_request_to_A2pSpear("spear:request/applicationdocument-responsibleauthorityreferencenumber", uuid_str, args_dict)
        
        self.logger.log(NodeLogger.TRACE, "===================== sid072a (%s)" % (rs.pretty_print()))
        
        return rs
                    
    def send_applicationdocument_referralrequest_request_to_A2pSpear(self, uuid_str, url, spear_reference_number):
        """
        Create the request Resource - "Application Document (Referral Request)" (sid061a),
        send to Spear and return the result as a Resource.
        
        :param uuid_str: String - UUID of user request.
        :param url: String - URL of Spear application document
        :param spear_reference_number: String - the Spear application's reference number
        :rtype: Resource - Response from A2pSpear
        """
        
        self.logger.log(NodeLogger.TRACE, "===================== Requesting sid061a (%s)" % (url))
        
        url_parts = urlparse.urlparse(str(url))
        
        args_dict = {}
        args_dict["documentUrlPath"] = url_parts.path
        args_dict["documentUrlQuery"] = url_parts.query
        args_dict["spearReferenceNumber"] = spear_reference_number
        rs = self.send_request_to_A2pSpear("spear:request/applicationdocument-referralrequest", uuid_str, args_dict)
        
        self.logger.log(NodeLogger.TRACE, "===================== sid061a (%s)" % (rs.pretty_print()))
        
        return rs
    
    def send_applicationdocument_request_to_A2pSpear(self, uuid_str, url, spear_reference_number):
        """
        Create the request Resource - "Application Document" (sid005a),
        send to Spear and return the result as a Resource.
        
        :param uuid_str: String - UUID of user request.
        :param url: String - URL of Spear application document
        :param spear_reference_number: String - the Spear application's reference number
        :rtype: Resource - Response from A2pSpear
        """
        
        self.logger.log(NodeLogger.TRACE, "===================== Requesting sid005a (%s)" % (url))
        
        url_parts = urlparse.urlparse(str(url))
        
        args_dict = {}
        args_dict["documentUrlPath"] = url_parts.path
        args_dict["documentUrlQuery"] = url_parts.query
        args_dict["spearReferenceNumber"] = spear_reference_number
        rs = self.send_request_to_A2pSpear("spear:request/applicationdocument", uuid_str, args_dict)
        
        self.logger.log(NodeLogger.TRACE, "===================== sid005a (%s)" % (rs.pretty_print()))
        
        return rs
    
    def send_applicationdocument_racomments_request_to_A2pSpear(self, uuid_str, url, spear_reference_number):
        """
        Create the request Resource - "Application Document (RA Comments)" (sid075c),
        send to Spear and return the result as a Resource.
        
        :param uuid_str: String - UUID of user request.
        :param url: String - URL of Spear application document
        :param spear_reference_number: String - the Spear application's reference number
        :rtype: Resource - Response from A2pSpear
        """
        
        self.logger.log(NodeLogger.TRACE, "===================== Requesting sid075c (%s)" % (url))
        
        url_parts = urlparse.urlparse(str(url))
        
        args_dict = {}
        args_dict["documentUrlPath"] = url_parts.path
        args_dict["documentUrlQuery"] = url_parts.query
        args_dict["spearReferenceNumber"] = spear_reference_number
        rs = self.send_request_to_A2pSpear("spear:request/applicationdocument-racomments", uuid_str, args_dict)
        
        self.logger.log(NodeLogger.TRACE, "===================== sid075c (%s)" % (rs.pretty_print()))
        
        return rs
    
    def send_applicationdocument_applicationforplanningpermit_request_to_A2pSpear(self, uuid_str, url, spear_reference_number):
        """
        Create the request Resource - "Application Document (Application for Planning Permit)" (sid006a),
        send to Spear and return the result as a Resource.
        
        :param uuid_str: String - UUID of user request.
        :param url: String - URL of Spear application document
        :param spear_reference_number: String - the Spear application's reference number
        :rtype: Resource - Response from A2pSpear
        """

        self.logger.log(NodeLogger.TRACE, "===================== Requesting sid006a (%s)" % (url))
        
        url_parts = urlparse.urlparse(str(url))
        
        args_dict = {}
        args_dict["documentUrlPath"] = url_parts.path
        args_dict["documentUrlQuery"] = url_parts.query
        args_dict["spearReferenceNumber"] = spear_reference_number
        rs = self.send_request_to_A2pSpear("spear:request/applicationdocument-applicationforplanningpermit", uuid_str, args_dict)
    
        self.logger.log(NodeLogger.TRACE, "===================== sid006a (%s)" % (rs.pretty_print()))
        
        return rs
    
    def send_applicationdocument_pdf_request_to_A2pSpear(self, uuid_str, url, spear_reference_number):
        """
        Create the request Resource - "Application Document (PDF)",
        send to Spear and return the result as a Resource.
        
        :param uuid_str: String - UUID of user request.
        :param url: String - URL of Spear application document
        :param spear_reference_number: String - the Spear application's reference number
        :rtype: Resource - Response from A2pSpear
        """
        
        self.logger.log(NodeLogger.TRACE, "===================== Requesting PDF (%s)" % (url))
        
        url_parts = urlparse.urlparse(str(url))
        
        args_dict = {}
        args_dict["documentUrlPath"] = url_parts.path
        args_dict["documentUrlQuery"] = url_parts.query
        args_dict["spearReferenceNumber"] = spear_reference_number
        rs = self.send_request_to_A2pSpear("spear:request/applicationdocument-pdf", uuid_str, args_dict)
        
        self.logger.log(NodeLogger.TRACE, "===================== PDF (%s)" % (rs))
        
        return rs
    
    def send_request_to_A2pSpear(self, a2pspear_request_uri_str, uuid_str, args_dict):
        """
        This function creates a Resource containing the argument A2PSpear request
        Uri, UUID and arguments dictionary and sends it to the A2PSpearService for
        sending to Spear.
        
        :param a2pspear_request_uri_str: String
        :param uuid_str: String - UUID of user request.
        :param args_dict: Dictionary
        :rtype: Resource - Response from A2pSpear
        """
        
        rs = Resource(type='term:eplanning/spear/a2pspear_service/a2pspearrequest')
        rs.set_var('a2pspear:request/uri', a2pspear_request_uri_str)
        rs.set_var('a2pspear:request/uuid', uuid_str)
        rs.set_var("arguments_dict", args_dict)
        response_uri = self.spaces.put(Uri('internal:eplanning/spear/a2pspear_service/a2pspearrequest'), rs)
        response_rs = self.spaces.get(response_uri)
        
        self.spaces.delete(response_uri)
        
        return response_rs
    
    @staticmethod
    def create_user_response_resource(request_uri, uuid_str, args_dict={}, error=None):
        """
        This function creates a user-response Resource containing the arguments and returns.
        
        :param request_uri: Uri - the user's request
        :param uuid_str: String - UUID of user request.
        :param args_dict: Dictionary - Response data.
        :param error: String - Error information
        :rtype: Resource
        """
        
        rs = Resource(type='term:eplanning/spear/a2p_service/a2presponse')
        rs.set_var('a2p:request/uri', request_uri)
        rs.set_var('a2p:request/uuid', uuid_str)
        if (error is not None):
            rs.set_var("error", error)
        
        for key in args_dict.keys():
            rs.set_var(key, args_dict[key])
        
        return rs
    
    def send_response_to_user(self, response_uri, request_uri, uuid_str, args_dict={}, error=None):
        """
        This function creates a Resource containing the arguments and sends it to the user.
        
        :param response_uri: Uri - Uri for spaces::put()
        :param request_uri: Uri - the user's request
        :param uuid_str: String - UUID of user request.
        :param args_dict: Dictionary - Response data.
        :param error: String - Error information
        """
        
        if (response_uri is not None):
            self.logger.log(logging.DEBUG, "Sending response to user....")
            
            rs = A2PService.create_user_response_resource(request_uri, uuid_str, args_dict, error)
            self.spaces.put(response_uri, rs)
        else:
            self.logger.log(logging.DEBUG, "No response sent to user.")
    
    def create_a2p_control_resource_in_spaces(self, last_poll_time):
        """
        Create a new control Resource and put() it to Spaces after deleting any 
        Spaces Resource that may already exist. The URI for the Resource in Spaces
        is::
        
            spear:a2p-control
        
        :param last_poll_time: datetime - Last poll timer
        """
        
        self.logger.log(NodeLogger.TRACE, "Creating control Resource")
        
        control_rs_uri = A2PService.create_a2p_control_uri()
        self.spaces.delete(control_rs_uri)
        
        control_rs = Resource(type='term:eplanning/spear/a2p-control')
        control_rs.set_metadata(Uri('term:base/runtime/active'), False)
        control_rs.set_var('last_poll_time', last_poll_time.strftime("%d/%m/%Y %H:%M:%S"))
        
        self.spaces.put(control_rs_uri, control_rs)
    
    def create_application_resource_in_spaces(self, spear_reference_number, spear_app_id, state, is_current=False, sid002b_dict={}):
        """
        Create a new application Resource for the argument Spear Reference Number
        and put() it to Spaces after deleting any Spaces Resource that may already 
        exist for that Spear Reference Number. The URI for the Resource in Spaces is::
        
            spear:app/<spear reference number>
        
        :param spear_reference_number: String - the Spear application's reference number
        :param spear_app_id: String - the Spear application's token id.
        :param state: String - The A2P state of the application Resource.
        :param sid002b_dict: Dictionary - data from sid002b (the application list page)
        """
        
        self.logger.log(NodeLogger.TRACE, "Creating application Resource, spear_reference_number (%s)" % (spear_reference_number))
        
        app_rs = Resource(type='term:eplanning/spear/application')
        app_rs.set_metadata(Uri('term:base/runtime/active'), False)
        app_rs.set_metadata(Uri('spearReferenceNumber'), spear_reference_number)
        app_rs.set_var('sid002b_dict', sid002b_dict)
        app_rs.set_var('outstanding_attachments_list', [])
        
        A2PService.set_application_resource_date_created(app_rs)
        A2PService.set_application_resource_date_last_modified(app_rs)
        A2PService.set_application_resource_spear_app_token(app_rs, spear_app_id)
        A2PService.set_application_resource_state(app_rs, state)
        A2PService.set_application_resource_current(app_rs, is_current)
        
        A2PService.put_application_resource_to_spaces(self.spaces, spear_reference_number, app_rs)
        
        return app_rs
    
    def create_application_attachment_resource_in_spaces(self, spear_reference_number, attachment_doc_id, pdf_data):
        """
        Create a new application attachment Resource for the argument Spear 
        Reference Number and put() it to Spaces after deleting any Spaces Resource
        that may already exist for that Spear Reference Number. The URI for the 
        Resource in Spaces is::
        
            spear:attachment/<Spear attachment document id>
        
        :param spear_reference_number: String - the Spear application's reference number
        :param attachment_doc_id: String - the Spear application's attachment document id
        :param pdf_data: Data - Base64-encoded PDF attachment contents.
        """
        
        self.logger.log(NodeLogger.TRACE, "Creating attachment Resource, spear_reference_number (%s), attachment_doc_id (%s)" % (spear_reference_number, attachment_doc_id))
        
        spearapp_attach_rs_uri = A2PService.create_spear_application_attachment_uri(attachment_doc_id)
        self.spaces.delete(spearapp_attach_rs_uri)
        
        app_attach_rs = Resource(type='term:eplanning/spear/application-attachment')
        app_attach_rs.set_metadata(Uri('term:base/runtime/active'), False)
        app_attach_rs.set_metadata(Uri('spearReferenceNumber'), spear_reference_number)
        app_attach_rs.set_metadata(Uri('documentId'), attachment_doc_id)
        app_attach_rs.set_var('date_created', datetime.now().strftime("%d/%m/%Y %H:%M:%S"))
        app_attach_rs.set_var('content_mime_type', "application/pdf")
        app_attach_rs.set_var('pdf', pdf_data)
        
        self.spaces.put(spearapp_attach_rs_uri, app_attach_rs)
    
    def create_user_list_resource_in_spaces(self, sid102a_dict_list=[]):
        """
        Create a new user list Resource and put() it to Spaces after deleting
        any user list Spaces Resource that may already exist. The URI for the 
        Resource in Spaces is::
        
            spear:user-list
        
        :param sid102a_dict_list: List - data from sid102a (the user list page)
        """
        
        self.logger.log(NodeLogger.TRACE, "Creating user list Resource")
        
        output_user_dict_list = []
        
        # Filter the list of users for those with a status 'Active' or 'Temporary Inactive'
        #
        if (len(sid102a_dict_list) > 0):
            for user_dict in sid102a_dict_list:
                user_status = user_dict["sid102a_user_status"].lower()
                if (user_status == "active") or \
                   (user_status == "temporary inactive"):
                    output_user_dict_list.append(user_dict)

        user_list_rs = Resource(type='term:eplanning/spear/user-list')
        user_list_rs.set_metadata(Uri('term:base/runtime/active'), False)
        user_list_rs.set_var('date_created', datetime.now().strftime("%d/%m/%Y %H:%M:%S"))
        user_list_rs.set_var('user_list', output_user_dict_list)
        
        # We keep only one list in Spaces.
        #
        user_list_uri = A2PService.create_spear_user_list_uri()
        self.spaces.put(user_list_uri, user_list_rs)
    
    def create_ra_list_resource_in_spaces(self, sid112a_dict_list=[]):
        """
        Create a new RA list Resource and put() it to Spaces after deleting
        any RA list Spaces Resource that may already exist. The URI for the 
        Resource in Spaces is::
        
            spear:ra-list
        
        :param sid112a_dict_list: List - data from sid102a (the user list page)
        """
        
        self.logger.log(NodeLogger.TRACE, "Creating RA list Resource")
        
        ra_list_rs = Resource(type='term:eplanning/spear/ra-list')
        ra_list_rs.set_metadata(Uri('term:base/runtime/active'), False)
        ra_list_rs.set_var('date_created', datetime.now().strftime("%d/%m/%Y %H:%M:%S"))
        ra_list_rs.set_var('ra_list', sid112a_dict_list)
        
        # We keep only one list in Spaces.
        #
        ra_list_uri = A2PService.create_spear_ra_list_uri()
        self.spaces.put(ra_list_uri, ra_list_rs)
    
    @staticmethod
    def get_a2p_control_resource_from_spaces(spaces):
        """
        Return the control Resource in Spaces.
        
        :rtype: Resource - control Resource or None
        """
        
        control_rs = None
        
        control_rs_uri = A2PService.create_a2p_control_uri()
        if (spaces.exists(control_rs_uri) is True):
            control_rs = spaces.get(control_rs_uri)
        
        return control_rs
    
    @staticmethod
    def get_application_resource_from_spaces(spaces, spear_reference_number):
        """
        Return any application Resource in Spaces with a Spear reference number
        matching the argument.
        
        :param spear_reference_number: String - the Spear application's reference number
        :rtype: Resource - application Resource or None
        """
        
        app_rs = None
        
        app_uri = A2PService.create_spear_application_uri(spear_reference_number)
        if (spaces.exists(app_uri) is True):
            app_rs = spaces.get(app_uri)
        
        return app_rs

    @staticmethod
    def put_application_resource_to_spaces(spaces, spear_reference_number, new_app_rs):
        """
        Write the argument application Resource 'app_rs' to Spaces with a Uri
        containing the argument Spear reference number. If any Resource already 
        exists at that Uri then we must copy some data from that Resource to the
        new one, then delete that Resource before writing.
        
        :param spear_reference_number: String - the Spear application's reference number
        :param app_rs: Resource - the application Resource
        :rtype: Uri - The Uri the Resource was stored in Spaces at.
        """
        
        app_uri = A2PService.create_spear_application_uri(spear_reference_number)
        
        if (spaces.exists(app_uri) is True):
            current_app_rs = spaces.get(app_uri)
            
            # We require some Spear application data to be saved when the 
            # Resource is being updated.
            #
            if (current_app_rs.has_var("application") is True) and \
               (new_app_rs.has_var("application") is True):
                current_rs_application_dict = current_app_rs.get_var("application")
                new_rs_application_dict = new_app_rs.get_var("application")
                
                # Note: 21/07/10 - 'Accepted_Flag' has been removed. May be added again
                #                  at a later date when better understood.
                #
                
                # We are pulling applications from the Spear 'current' applications
                # list. Because they are current they are NOT complete.
                #
                # Note: 21/07/10 - This 'Completed_Flag' functionality may be 
                #                  changed at a later date.
                #
                new_rs_application_dict["Completed_Flag"] = current_rs_application_dict["Completed_Flag"]
            
            A2PService.delete_application_resource_from_spaces(spaces, spear_reference_number)
        
        return spaces.put(A2PService.create_spear_application_uri(spear_reference_number), new_app_rs)
    
    @staticmethod
    def delete_application_resource_from_spaces(spaces, spear_reference_number):
        """
        Delete any Resource in Spaces with a Uri containing the argument Spear
        reference number.
        
        :param spear_reference_number: String - the Spear application's reference number
        """
        
        app_uri = A2PService.create_spear_application_uri(spear_reference_number)
        if (spaces.exists(app_uri) is True):
            # delete attachments.
            #
            app_rs = spaces.get(app_uri)
            if (app_rs is not None) and \
               (app_rs.has_var("attachments") is True):
                attachments_dict = app_rs.get_var("attachments")
                for doc_type in attachments_dict:
                    for doc_id in attachments_dict[doc_type]:
                        attachment_uri = A2PService.create_spear_application_attachment_uri(doc_id)
                        if (spaces.exists(attachment_uri) is True):
                            spaces.delete(attachment_uri)
            
            spaces.delete(app_uri)
    
    @staticmethod
    def create_a2p_control_uri():
        """
        Create the Spaces Uri for Spear control Resource.
        
        :rtype: Uri - the Spear application Uri
        """
        
        return Uri('spear:a2p-control')
    
    @staticmethod
    def create_spear_application_uri(spear_reference_number):
        """
        Create the Spaces Uri for Spear application Resources.
        
        :param spear_reference_number: String - the Spear application's reference number
        :rtype: Uri - the Spear application Uri
        """
        
        return Uri('spear:app/') + spear_reference_number
    
    @staticmethod
    def create_spear_application_attachment_uri(document_id):
        """
        Create the Spaces Uri for Spear application Resources.
        
        :param document_id: String - the document Id of the attachment within Spear
        :rtype: Uri - the Spear attachment Uri
        """
        
        return Uri('spear:app-attachment/') + document_id
    
    @staticmethod
    def create_spear_user_list_uri():
        """
        Create the Spaces Uri for Spear user list.
        
        :rtype: Uri - the Spear user list Uri
        """
        
        return Uri('spear:user-list')
    
    @staticmethod
    def create_spear_ra_list_uri():
        """
        Create the Spaces Uri for Spear RA list.
        
        :rtype: Uri - the Spear RA list Uri
        """
        
        return Uri('spear:ra-list')
    
    @staticmethod
    def set_application_resource_state(app_rs, state):
        """
        This function sets the "state" attribute of the argument application
        Resource.
        
        :param app_rs: Resource - the Application Resource
        :param state: String
        """
        
        control_dict = {}
        if (app_rs.has_var('control_dict') is True):
            control_dict = app_rs.get_var('control_dict')
        control_dict['state'] = state
        app_rs.set_var('control_dict', control_dict)
    
    @staticmethod
    def set_application_resource_current(app_rs, is_current=False):
        """
        This function sets the "current_flag" attribute of the argument application
        Resource. We set this in the Resource's metadata so it can be searched for
        in the DataStore.
        
        :param app_rs: Resource - the Application Resource
        :param is_current: Boolean
        """
        
        app_rs.set_metadata(Uri('is_current'), is_current)
    
    @staticmethod
    def set_application_resource_date_created(app_rs):
        """
        This function sets the "date_created" attribute of the argument 
        application Resource to now().
        
        :param app_rs: Resource - the Application Resource
        """
        
        control_dict = {}
        if (app_rs.has_var('control_dict') is True):
            control_dict = app_rs.get_var('control_dict')
        control_dict['date_created'] = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
        app_rs.set_var('control_dict', control_dict)
    
    @staticmethod
    def set_application_resource_date_last_modified(app_rs):
        """
        This function sets the "date_last_modified" attribute of the argument 
        application Resource to now().
        
        :param app_rs: Resource - the Application Resource
        """
        
        control_dict = {}
        if (app_rs.has_var('control_dict') is True):
            control_dict = app_rs.get_var('control_dict')
        control_dict['date_last_modified'] = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
        app_rs.set_var('control_dict', control_dict)
    
    @staticmethod
    def set_application_resource_spear_app_token(app_rs, spear_app_token=""):
        """
        This function sets the "spear_app_token" attribute of the argument 
        application Resource to the argument application token.
        
        :param app_rs: Resource - the Application Resource
        """
        
        control_dict = {}
        if (app_rs.has_var('control_dict') is True):
            control_dict = app_rs.get_var('control_dict')
        control_dict['spear_app_token'] = spear_app_token
        app_rs.set_var('control_dict', control_dict)
    
    def get_urlparameter(self, qs_parameter, qs):
        """
        This function processes the argument URL query string 'qs' for the value
        of the argument 'qs_parameter'. 
        
        :param qs_parameter: String
        :param qs: String
        :rtype: String - the value of 'qs_parameter'. 
        """
        
        return_string = ""
        
        if (len(qs) > 0):
            qs_parts_list = qs.split("&")
            for keyvalue in qs_parts_list:
                key, value = keyvalue.split("=")
                if (key == qs_parameter):
                    return_string = value
                    break
        
        return return_string

    def do_derivations(self, app_rs):
        """
        Derive the data required by eDAIS from the Spear application data stored
        in the argument Resource 'app_rs'. The derivations are stored in 'app_rs' 
        according to the Spear/eDAIS mapping spreadsheet.
        
        :param app_rs: Resource - the Spear application data.
        """
        
        self.logger.log(logging.DEBUG, "")
        
        rs_application_dict = {}
        rs_referrals_dict_list = []
        rs_attachments_dict = {}
        rs_mandatory_action_list = []
        rs_optional_action_list = []
        
        sid002b_dict = {}
        if (app_rs.has_var("sid002b_dict")):
            sid002b_dict = app_rs.get_var("sid002b_dict")
        
        sid004_dict = {}
        sid004_attachment_dict_list = []
        if (app_rs.has_var("sid004_dict")):
            sid004_dict = app_rs.get_var("sid004_dict")
            sid004_attachment_dict_list = self.get_list(sid004_dict, "sid004_attachment_dict_list")
            
        sid005a_dict_dict = {}
        if (app_rs.has_var("sid005a_dict_dict")):
            sid005a_dict_dict = app_rs.get_var("sid005a_dict_dict")
        
        sid006a_dict = {}
        if (app_rs.has_var("sid006a_dict")):
            sid006a_dict = app_rs.get_var("sid006a_dict")
        
        sid061a_dict_dict = {}
        if (app_rs.has_var("sid061a_dict_dict")):
            sid061a_dict_dict = app_rs.get_var("sid061a_dict_dict")
        
        sid072a_dict = {}
        if (app_rs.has_var("sid072a_dict")):
            sid072a_dict = app_rs.get_var("sid072a_dict")
        
        sid075c_dict_dict = {}
        if (app_rs.has_var("sid075c_dict_dict")):
            sid075c_dict_dict = app_rs.get_var("sid075c_dict_dict")
        
        # We are pulling applications from the Spear 'current' applications list. 
        # Because they are current they are NOT complete.
        #
        # Note: 21/07/10 - This 'Completed_Flag' functionality may be changed at a later date.
        #
        rs_application_dict["Completed_Flag"] = False
        
        # Note: 21/07/10 - 'Accepted_Flag' has been removed. May be added again
        #                  at a later date when better understood.
        #
        
        # sid002b...
        #
        rs_application_dict["Application_Status"] = self.get_string(sid002b_dict, "sid002b_status")
        
        # The application version. This can be used to determine if the application
        # has been modified.
        #
        # Note: This is NOT in the spreadsheet.
        #
        rs_application_dict["Application_Version"] = self.get_string(sid002b_dict, "sid002b_cc_version")
        
        # sid004...
        #
        rs_mandatory_action_list = self.get_list(sid004_dict, "sid004_current_actors_mandatory_action_list")
        rs_optional_action_list = self.get_list(sid004_dict, "sid004_optional_action_list")
        
        for attachment_dict in sid004_attachment_dict_list:
            # We add to rs_attachments_dict only those documents that are in sid005a_dict_dict,
            # that is, those application documents that clickthru to the document meta-data page.
            #
            # rs_attachments_dict is a dictionary, keyed by document-type, where each element
            # is a dictionary (of dictionaries). Each element dictionary is keyed by the 
            # attachment's document-id. 
            #
            
            sid004_doc_id = attachment_dict["sid004_doc_id"]
            
            # Find the meta-data (sid005a) for this attachment...
            #
            sid005a_dict = {}
            for attachment_document_id, sid005a_dict in sid005a_dict_dict.iteritems():

                if (attachment_document_id == sid004_doc_id):
                    # We don't store all doc-types in "attachments", just the following:
                    # - "Advertising Instructions"
                    # - "Decision - Amended Planning Permit" (stored as "Planning Permit")
                    # - "Decision - Planning Permit" (stored as "Planning Permit")
                    # - "RA Response"
                    # - "Request for Further Information"
                    # - "Attachment" - all other doc-types 
                    #
                    
                    doc_type = "Attachment"
                    if (attachment_dict["sid004_doctype"] == "Decision - Amended Planning Permit") or \
                       (attachment_dict["sid004_doctype"] == "Decision - Planning Permit"):
                        doc_type = "Planning Permit"
                    elif (attachment_dict["sid004_doctype"] == "Advertising Instructions") or \
                         (attachment_dict["sid004_doctype"] == "RA Response") or \
                         (attachment_dict["sid004_doctype"] == "Request for Further Information"):
                        doc_type = attachment_dict["sid004_doctype"]
                    
                    d = {}
                    if (doc_type not in rs_attachments_dict):
                        rs_attachments_dict[doc_type] = {}
                    rs_attachments_dict[doc_type][sid004_doc_id] = d
                    
#                    if (attachment_dict["sid004_doctype"] not in rs_attachments_dict):
#                        rs_attachments_dict[attachment_dict["sid004_doctype"]] = {}
#                    rs_attachments_dict[attachment_dict["sid004_doctype"]][sid004_doc_id] = d
                    
                    
#                    # sid004...
#                    #
#                    if (attachment_dict["sid004_owner_type__derived"] == "Applicant Contact") and \
#                       ("sid006a_applicant_contact_organisation_name" in sid006a_dict):
#                        if (self.get_stringlength(sid006a_dict, "sid006a_applicant_contact_organisation_name") > 0):
#                            d["Owner_Organisation_Name"] = self.get_string(attachment_dict, "sid004_owner__derived")
#                            d["Owner_ApplicantContact_First_Name"] = ""
#                            d["Owner_ApplicantContact_Last_Name"] = ""
#                        else:
#                            d["Owner_Organisation_Name"] = ""
#                            d["Owner_ApplicantContact_First_Name"] = self.get_name_firstname(attachment_dict, "sid004_owner__derived")
#                            d["Owner_ApplicantContact_Last_Name"] = self.get_name_lastname(attachment_dict, "sid004_owner__derived", is_organisation=False)
#                    
#                    if (attachment_dict["sid004_owner_type__derived"] == "Responsible Authority"):
#                        d["Owner_Organisation_Name"] = self.get_string(attachment_dict, "sid004_owner__derived")
#                        d["Owner_ApplicantContact_First_Name"] = ""
#                        d["Owner_ApplicantContact_Last_Name"] = ""
#                    
#                    if (attachment_dict["sid004_owner_type__derived"] == "Referral Authority") or \
#                       (attachment_dict["sid004_owner_type__derived"] == "Online Objector") or \
#                       (attachment_dict["sid004_owner_type__derived"] == "VCAT"):
#                        d["Owner_Organisation_Name"] = ""
#                        d["Owner_ApplicantContact_First_Name"] = ""
#                        d["Owner_ApplicantContact_Last_Name"] = ""
                    
                    # sid005a...
                    
                    if sid005a_dict.has_key('sid005a_current_document_doctype'):
                        sid005a_current_document_doctype = sid005a_dict["sid005a_current_document_doctype"]
                    else:
                        sid005a_current_document_doctype = ''
                    # Advertising Instructions - Council
                    #
                    if (sid005a_current_document_doctype.find("Advertising Instructions") > -1):
                        d["Authenticated_By_First_Name"] = self.get_name_firstname(sid005a_dict, "sid005a_current_document_authenticated_by")
                        d["Authenticated_By_Last_Name"] = self.get_name_lastname(sid005a_dict, "sid005a_current_document_authenticated_by", is_organisation=True)
                        d["Authenticated_By_Organisation_Name"] = self.get_name_organisation(sid005a_dict, "sid005a_current_document_authenticated_by")
                        d["Format"] = self.get_string(sid005a_dict, "sid005a_current_document_format")
                        d["Outcome"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=1)
                        d["Owner_Type"] = attachment_dict["sid004_owner_type__derived"]
                        d["Prefix"] = self.get_string(sid005a_dict, "sid005a_current_document_prefix")
                        d["Section"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
                        d["Size"] = self.get_size(sid005a_dict, "sid005a_current_document_size")
                        d["Submitted_Date"] = self.get_string(sid005a_dict, "sid005a_current_document_submitted_date")
                        d["Suffix"] = self.get_string(sid005a_dict, "sid005a_current_document_suffix")
                        d["URL"] = ""
                        d["Version_Description"] = self.get_string(sid005a_dict, "sid005a_current_document_version_description")
                        d["Version_Number"] = self.get_string(sid005a_dict, "sid005a_current_document_version_number")
                        
                        d["Doc_Id"] = attachment_document_id
                        d["Doc_Type"] = sid005a_current_document_doctype
                    
                    # Decision - Amended Planning Permit - Council
                    # Decision - Planning Permit - Council
                    #
                    elif (sid005a_current_document_doctype.find("Planning Permit") > -1):
                        d["Authenticated_By_First_Name"] = self.get_name_firstname(sid005a_dict, "sid005a_current_document_authenticated_by")
                        d["Authenticated_By_Last_Name"] = self.get_name_lastname(sid005a_dict, "sid005a_current_document_authenticated_by", is_organisation=True)
                        d["Authenticated_By_Organisation_Name"] = self.get_name_organisation(sid005a_dict, "sid005a_current_document_authenticated_by")
                        d["Format"] = self.get_string(sid005a_dict, "sid005a_current_document_format")
                        d["Outcome"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=1)
                        d["Owner_Type"] = attachment_dict["sid004_owner_type__derived"]
                        d["Prefix"] = self.get_string(sid005a_dict, "sid005a_current_document_prefix")
                        d["Required_Action_for_Endorsement"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=2)
                        d["Size"] = self.get_size(sid005a_dict, "sid005a_current_document_size")
                        d["Submitted_Date"] = self.get_string(sid005a_dict, "sid005a_current_document_submitted_date")
                        d["Suffix"] = self.get_string(sid005a_dict, "sid005a_current_document_suffix")
                        d["URL"] = ""
                        d["Version_Description"] = self.get_string(sid005a_dict, "sid005a_current_document_version_description")
                        d["Version_Number"] = self.get_string(sid005a_dict, "sid005a_current_document_version_number")
                        
                        d["Doc_Id"] = attachment_document_id
                        d["Doc_Type"] = sid005a_current_document_doctype
                    
                    # RA Response - Council (on behalf of RA), RA
                    #
                    elif (sid005a_current_document_doctype.find("RA Response") > -1):
                        if (sid005a_dict["sid005a_current_document_responseoutcomesuffixcommand_suffix"].find("Section 55") > -1):
                            d["Abridged_Response_Outcome"] = self.get_suffix_section55_outcome(sid005a_dict, "sid005a_current_document_responseoutcomesuffixcommand_suffix")
                        elif (sid005a_dict["sid005a_current_document_responseoutcomesuffixcommand_suffix"].find("Certification") > -1):
                            d["Abridged_Response_Outcome"] = self.get_suffix_section8Cert_outcome(sid005a_dict, "sid005a_current_document_responseoutcomesuffixcommand_suffix")
                        elif (sid005a_dict["sid005a_current_document_responseoutcomesuffixcommand_suffix"].find("SOC") > -1):
                            d["Abridged_Response_Outcome"] = self.get_suffix_section8SOC_outcome(sid005a_dict, "sid005a_current_document_responseoutcomesuffixcommand_suffix")
                        
                        d["Authenticated_By_First_Name"] = self.get_name_firstname(sid005a_dict, "sid005a_current_document_authenticated_by")
                        d["Authenticated_By_Last_Name"] = self.get_name_lastname(sid005a_dict, "sid005a_current_document_authenticated_by", is_organisation=True)
                        d["Authenticated_By_Organisation_Name"] = self.get_name_organisation(sid005a_dict, "sid005a_current_document_authenticated_by")
                        d["Format"] = self.get_string(sid005a_dict, "sid005a_current_document_format")
                        d["Owner_Type"] = attachment_dict["sid004_owner_type__derived"]
                        d["Prefix"] = self.get_string(sid005a_dict, "sid005a_current_document_prefix")
                        
                        # relate any RA-comments to a referral response.
                        #
                        for ra_response_document_id, sid075c_dict in sid075c_dict_dict.iteritems():
                            if (ra_response_document_id == sid004_doc_id):
                                d["RA_Comments"] = sid075c_dict["sid075c_comments"]
                                break
                        
                        # Suffix date format - dd/mm/YYYY
                        # Suffix time format - HH:MM
                        # Output date/time format - YYYY-mm-ddTHH:MM:SS
                        #
                        try:
                            dt_date = datetime.strptime(self.get_string_part(sid005a_dict, "sid005a_current_document_responseoutcomesuffixcommand_suffix", separator=' ', index=0), "%d/%m/%Y")
                            dt_time = datetime.strptime(self.get_string_multipart(sid005a_dict, "sid005a_current_document_responseoutcomesuffixcommand_suffix", separator=' ', start_index=1, end_index=3).replace(",", ""), "%I:%M %p")
                            dt = datetime.combine(dt_date.date(), dt_time.time())
                            d["Referral_DateTime"] = dt.strftime("%Y-%m-%dT%H:%M:%S")
                        except ValueError:
                            d["Referral_DateTime"] = ""
                            
#                        d["Referral_DateTime"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=0)
#                        d["Suffix_RA_Response_Referral_Time"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=1).replace(",", "")
                        
                        d["Size"] = self.get_size(sid005a_dict, "sid005a_current_document_size")
                        d["Submitted_Date"] = self.get_string(sid005a_dict, "sid005a_current_document_submitted_date")
                        d["Suffix"] = self.get_string(sid005a_dict, "sid005a_current_document_responseoutcomesuffixcommand_suffix")
                        d["URL"] = ""
                        d["Version_Description"] = self.get_string(sid005a_dict, "sid005a_current_document_version_description")
                        d["Version_Number"] = self.get_string(sid005a_dict, "sid005a_current_document_version_number")
                        
                        d["Doc_Id"] = attachment_document_id
                        d["Doc_Type"] = sid005a_current_document_doctype
                    
                    # Request for Further Information - Council, RA
                    #
                    elif (sid005a_current_document_doctype.find("Request for Further Information") > -1):
                        d["Authenticated_By_First_Name"] = self.get_name_firstname(sid005a_dict, "sid005a_current_document_authenticated_by")
                        d["Authenticated_By_Last_Name"] = self.get_name_lastname(sid005a_dict, "sid005a_current_document_authenticated_by", is_organisation=True)
                        d["Authenticated_By_Organisation_Name"] = self.get_name_organisation(sid005a_dict, "sid005a_current_document_authenticated_by")
                        d["Due_Date"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
                        d["Format"] = self.get_string(sid005a_dict, "sid005a_current_document_format")
                        d["Owner_Type"] = attachment_dict["sid004_owner_type__derived"]
                        d["Prefix"] = self.get_string(sid005a_dict, "sid005a_current_document_prefix")
                        d["Size"] = self.get_size(sid005a_dict, "sid005a_current_document_size")
                        
                        d["Subject"] = ""
                        if (attachment_dict["sid004_owner_type__derived"] == "Responsible Authority"):
                            d["Subject"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=1)
                        
                        d["Submitted_Date"] = self.get_string(sid005a_dict, "sid005a_current_document_submitted_date")
                        d["Suffix"] = self.get_string(sid005a_dict, "sid005a_current_document_suffix")
                        d["URL"] = ""
                        d["Version_Description"] = self.get_string(sid005a_dict, "sid005a_current_document_version_description")
                        d["Version_Number"] = self.get_string(sid005a_dict, "sid005a_current_document_version_number")
                            
                        d["Doc_Id"] = attachment_document_id
                        d["Doc_Type"] = sid005a_current_document_doctype
                        
                    else:
                        # Here we're handling all other doc-types. 
                        #
                        
                        d["Authenticated_By_First_Name"] = self.get_name_firstname(sid005a_dict, "sid005a_current_document_authenticated_by")
                        d["Authenticated_By_Last_Name"] = self.get_name_lastname(sid005a_dict, "sid005a_current_document_authenticated_by", is_organisation=True)
                        d["Authenticated_By_Organisation_Name"] = self.get_name_organisation(sid005a_dict, "sid005a_current_document_authenticated_by")
                        d["Format"] = self.get_string(sid005a_dict, "sid005a_current_document_format")
                        
                        # Plan of Subdivision or Consolidation - Applicant
                        #
                        d["Modified_LV_Request"] = ""
                        if (sid005a_current_document_doctype.find("Plan of Subdivision or Consolidation") > -1):
                            if (sid005a_dict["sid005a_current_document_suffix"].find("modified at LV request") > -1):
                                d["Modified_LV_Request"] = "modified at LV request"
                        
                        # Extension of Time - Applicant
                        # - Number of days is the last field (space-delimited) of suffix.
                        #
                        d["Number_of_Days"] = ""
                        if (sid005a_current_document_doctype.find("Extension of Time") > -1):
                            d["Number_of_Days"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
                        
                        # Arborist report - Applicant
                        # Compiled Plan Sheets - Applicant
                        # Plan of Demolition - Applicant
                        # Plan of Subdivision or Consolidation - Applicant
                        # Site plan (proposed) - Applicant
                        # Surveyor's Report - Applicant
                        # - Number of pages is last field (space-delimited) of suffix.
                        #
                        d["Number_of_Pages"] = ""
                        if (sid005a_current_document_doctype.find("Arborist report") > -1) or \
                           (sid005a_current_document_doctype.find("Compiled Plan Sheets") > -1) or \
                           (sid005a_current_document_doctype.find("Plan of Demolition") > -1) or \
                           (sid005a_current_document_doctype.find("Plan of Subdivision or Consolidation") > -1) or \
                           (sid005a_current_document_doctype.find("Site plan (proposed)") > -1) or \
                           (sid005a_current_document_doctype.find("Surveyor's Report") > -1):
                            d["Number_of_Pages"] = self.get_suffix_number_of_pages(sid005a_dict, "sid005a_current_document_suffix")
                        
                        # Objection - Objectors
                        #
                        d["Objection_Number"] = ""
                        d["Objection_Withdrawn"] = ""
                        d["Online_Objector_First_Name"] = ""
                        d["Online_Objector_Last_Name"] = ""
                        if (sid005a_current_document_doctype.find("Objection") > -1):
                            d["Objection_Number"] = self.get_string(attachment_dict, "sid004_objection_number")
                            d["Objection_Withdrawn"] = self.get_string(attachment_dict, "sid004_objection_withdrawn")
                            d["Online_Objector_First_Name"] = self.get_name_firstname(sid005a_dict, "sid005a_current_document_authenticated_by")
                            d["Online_Objector_Last_Name"] = self.get_name_lastname(sid005a_dict, "sid005a_current_document_authenticated_by", is_organisation=False)
                        
                        # Application Acceptance / Rejection - Council
                        # - Outcome is whole suffix.
                        #
                        d["Outcome"] = ""
                        if (sid005a_current_document_doctype.find("Application Acceptance / Rejection") > -1):
                            d["Outcome"] = self.get_string(sid005a_dict, "sid005a_current_document_outcomesuffixcommand_suffix")
                        
                        d["Owner_Organisation_Name"] = ""
                        d["Owner_ApplicantContact_First_Name"] = ""
                        d["Owner_ApplicantContact_Last_Name"] = ""
                        if (attachment_dict["sid004_owner_type__derived"] == "Applicant Contact") and \
                           (self.get_stringlength(sid006a_dict, "sid006a_applicant_contact_organisation_name") == 0):
                            d["Owner_ApplicantContact_First_Name"] = self.get_name_firstname(attachment_dict, "sid004_owner__derived")
                            d["Owner_ApplicantContact_Last_Name"] = self.get_name_lastname(attachment_dict, "sid004_owner__derived", is_organisation=False)
                        
                        if (sid005a_current_document_doctype.find("Objection") == -1) and \
                           (self.get_stringlength(sid006a_dict, "sid006a_applicant_contact_organisation_name") > 0):
                            d["Owner_Organisation_Name"] = self.get_string(attachment_dict, "sid004_owner__derived")
                        
                        d["Owner_Type"] = attachment_dict["sid004_owner_type__derived"]
                        d["Prefix"] = self.get_string(sid005a_dict, "sid005a_current_document_prefix")
                        
                        # Pre-approved RA Consent - Applicant
                        #
                        d["RA_Name"] = ""
                        if (sid005a_current_document_doctype.find("Pre-approved RA Consent") > -1):
                            d["RA_Name"] = self.get_string(sid005a_dict, "sid005a_current_document_suffix")
                            
                        # Advertising Notice to Referral Authorities - Council
                        # Submission to Advertising Notice - RA
                        # - Section number is the last field (space-delimited) of suffix.
                        #
                        d["Section"] = ""
                        if (sid005a_current_document_doctype.find("Advertising Notice to Referral Authorities") > -1) or \
                           (sid005a_current_document_doctype.find("Submission to Advertising Notice") > -1):
                            d["Section"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
                        
                        d["Size"] = self.get_size(sid005a_dict, "sid005a_current_document_size")
                        
                        # Extension of Time - Applicant
                        # - Subject is the 2nd-field (comma-delimited) of suffix.
                        #
                        d["Subject"] = ""
                        if (sid005a_current_document_doctype.find("Extension of Time") > -1):
                            d["Subject"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=1)
                        
                        # Response to Request for Further Information - Applicant
                        # - Subject is all fields after first comma of suffix.
                        #
                        elif (sid005a_current_document_doctype.find("Response to Request for Further Information") > -1):
                            d["Subject"] = self.get_string_multipart(sid005a_dict, "sid005a_current_document_suffix", separator=',', start_index=1, end_index=-1)
                        
                        d["Submitted_Date"] = ""
                        if (attachment_dict["sid004_owner_type__derived"] == "Applicant Contact"):
                            d["Submitted_Date"] = self.get_string(sid005a_dict, "sid005a_current_document_submitted_date")
                        
                        d["Suffix"] = self.get_string(sid005a_dict, "sid005a_current_document_suffix")
                        
                        # Plan for Endorsement / Endorsed Plan - Applicant
                        # Plan of Subdivision or Consolidation - Applicant
                        # Proposed Plan (LV Modifications) - Applicant
                        # Proposed Plan of Subdivision for Re-Certification - Applicant
                        #
                        d["Surveyor_Plan_Version"] = ""
                        if (sid005a_current_document_doctype.find("Endorsed Plan") > -1) or \
                           (sid005a_current_document_doctype.find("Plan for Endorsement") > -1) or \
                           (sid005a_current_document_doctype.find("Plan of Subdivision or Consolidation") > -1) or \
                           (sid005a_current_document_doctype.find("Proposed Plan (LV Modifications)") > -1) or \
                           (sid005a_current_document_doctype.find("Proposed Plan of Subdivision for Re-Certification") > -1):
                            d["Surveyor_Plan_Version"] = self.get_suffix_surveyors_plan_version(sid005a_dict, "sid005a_current_document_suffix")
                        
                        d["URL"] = ""
                        d["Version_Description"] = self.get_string(sid005a_dict, "sid005a_current_document_version_description")
                        d["Version_Number"] = self.get_string(sid005a_dict, "sid005a_current_document_version_number")
                        
                        # Copy of Title - Applicant
                        # - Vol/Fol are all but 1st-field (space-delimited) of suffix.
                        #
                        d["VolFol"] = ""
                        if (sid005a_current_document_doctype.find("Copy of Title") > -1):
                            d["VolFol"] = self.get_string_multipart(sid005a_dict, "sid005a_current_document_suffix", separator=' ', start_index=1, end_index=-1)
                            
                        
                        d["Doc_Id"] = attachment_document_id
                        d["Doc_Type"] = sid005a_current_document_doctype
                        
                        # Other Document Type - Applicant, Council, RA
                        # - doc-type description is whole of suffix.
                        #
                        d["Doc_Type_Description"] = ""
                        if (sid005a_current_document_doctype.find("Other Document Type") > -1):
                            d["Doc_Type_Description"] = self.get_string(sid005a_dict, "sid005a_current_document_suffix")
                            
                    
                    
                    
#                     Note: When a planning permit is being issued applicant attachments are endorsed. 
#                           The 'doctype' of some of these attachments goes from "<doctype>" to 
#                           "Endorsed <doctype>". 
#                    
#                     Note: Some sid005a_dicts will have empty "document_url"s (eg. Application 
#                           Acceptance/Rejection).
#                    
                    
#                    # Objection - Objectors
#                    #
#                    if (sid005a_current_document_doctype == "Objection"):
#                        d["Online_Objector_First_Name"] = self.get_name_firstname(sid005a_dict, "sid005a_current_document_authenticated_by")
#                        d["Online_Objector_Last_Name"] = self.get_name_lastname(sid005a_dict, "sid005a_current_document_authenticated_by", is_organisation=False)
                    
#                    d["URL"] = ""
#                    d["Prefix"] = self.get_string(sid005a_dict, "sid005a_current_document_prefix")
#                    d["doc_type"] = self.get_string(sid005a_dict, "sid005a_current_document_doctype")
#                    d["Suffix"] = self.get_string(sid005a_dict, "sid005a_current_document_suffix")
                    
#                    # Application Acceptance / Rejection - Council
#                    # - Outcome is whole suffix.
#                    #
#                    if (sid005a_current_document_doctype == "Application Acceptance / Rejection"):
#                        d["Suffix_Accept_Reject_Outcome"] = self.get_string(sid005a_dict, "sid005a_current_document_suffix")
                    
#                    # Request for Further Information - Council, RA
#                    # - Subject is 2nd-field (comma-delimited) of suffix.
#                    # - Due date is last field (space-delimited) of suffix.
#                    #
#                    if (sid005a_current_document_doctype == "Request for Further Information"):
#                        d["Suffix_Additional_Information_Requested_Subject"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=1)
#                        d["Suffix_Additional_Information_Requested_Due_Date"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
                    
#                     Advertising Instructions - Council
#                     - Section number is the last field (space-delimited) of suffix.
#                     - Outcome is 2nd-field (comma-delimited) of suffix.
#                    
#                    if (sid005a_current_document_doctype == "Advertising Instructions"):
#                        d["Suffix_Advertising_Instructions_Section"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
#                        d["Suffix_Advertising_Instructions_Outcome"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=1)
                    
#                     Advertising Notice to Referral Authorities - Council
#                     - Section number is the last field (space-delimited) of suffix.
#                    
#                    if (sid005a_current_document_doctype == "Advertising Notice to Referral Authorities"):
#                        d["Suffix_Advertising_Notice_to_Referral_Authorities_Section"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
                    
#                     Arborist report - Applicant
#                     - Number of pages is last field (space-delimited) of suffix.
#                    
#                    if (sid005a_current_document_doctype.find("Arborist report") > -1):
#                        d["Suffix_Arborist_Report_Number_of_Pages"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
                    
#                     Compiled Plan Sheets - Applicant
#                     - Number of pages is last field (space-delimited) of suffix.
#                    
#                    if (sid005a_current_document_doctype.find("Compiled Plan Sheets") > -1):
#                        d["Suffix_Compiled_Plan_Sheets_Number_of_Pages"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
                    
#                    # Copy of Title - Applicant
#                    # - Vol/Fol are all but 1st-field (space-delimited) of suffix.
#                    #
#                    if (sid005a_current_document_doctype.find("Copy of Title") > -1):
#                        d["Suffix_Copy_of_Title_VolFol"] = self.get_string_multipart(sid005a_dict, "sid005a_current_document_suffix", separator=' ', start_index=1, end_index=-1)
                    
#                    # Decision - Amended Planning Permit - Council
#                    # - Outcome is 2nd field (comma-delimited) of suffix.
#                    # - Required Action for Endorsement is 3rd field (comma-delimited) of suffix, if present.
#                    #
#                    if (sid005a_current_document_doctype.find("Decision - Amended Planning Permit") > -1):
#                        d["Suffix_Decision_Amended_Planning_Permit_Outcome"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=1)
#                        d["Suffix_Decision_Amended_Planning_Permit_Required_Action_for_Endorsement"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=2)
                        
#                    # Decision - Planning Permit - Council
#                    # - Outcome is 2nd field (comma-delimited) of suffix.
#                    # - Required Action for Endorsement is 3rd field (comma-delimited) of suffix, if present.
#                    #
#                    if (sid005a_current_document_doctype == "Decision - Planning Permit"):
#                        d["Suffix_Decision_Planning_Permit_Outcome"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=1)
#                        d["Suffix_Decision_Planning_Permit_Required_Action_for_Endorsement"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=2)
                    
#                     Extension of Time - Applicant
#                     - Subject is the 2nd-fields (comma-delimited) of suffix.
#                     - Number of days is the last field (space-delimited) of suffix.
#                    
#                    if (sid005a_current_document_doctype.find("Extension of Time") > -1):
#                        d["Suffix_Extension_of_Time_Subject"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=1)
#                        d["Suffix_Extension_of_Time_Number_of_Days"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
                    
#                     Other Document Type - Applicant, Council, RA
#                     - Whole of suffix.
#                    
#                    if (sid005a_current_document_doctype.find("Other Document Type") > -1):
#                        d["Suffix_Other_Document_Type_Doctype_Description"] = self.get_string(sid005a_dict, "sid005a_current_document_suffix")
                    
#                     Plan of Demolition - Applicant
#                     - Number of pages is last field (space-delimited) of suffix.
#                     
#                    if (sid005a_current_document_doctype.find("Plan of Demolition") > -1):
#                        d["Suffix_Plan_of_Demolition_Number_of_Pages"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
                    
#                    # Plan for Endorsement / Endorsed Plan - Applicant
#                    # - Surveyor's Plan Version is the last field (space-delimited) of suffix.
#                    #
#                    if (sid005a_current_document_doctype == "Endorsed Plan") or \
#                       (sid005a_current_document_doctype == "Plan for Endorsement"):
#                        d["Suffix_Plan_for_Endorsement_Surveyor_Plan_Version"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
                    
#                    # Plan of Subdivision or Consolidation - Applicant
#                    # - Number of pages is 4th-field (space-delimited) of suffix. Must trim comma from end.
#                    # - Surveyor's Plan Version is last field (space-delimited) of suffix. 
#                    #
#                    if (sid005a_current_document_doctype.find("Plan of Subdivision or Consolidation") > -1):
#                        d["Suffix_Plan_of_Subdivision_or_Consolidation_Number_of_Pages"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=3).replace(",", "")
#                        d["Suffix_Plan_of_Subdivision_or_Consolidation_Surveyor_Plan_Version"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
                        
#                        if (sid005a_dict["sid005a_current_document_suffix"].find("modified at LV request") > -1):
#                            d["Suffix_Plan_of_Subdivision_or_Consolidation_Modified_LV_Request"] = "modified at LV request"
                    
#                    # Pre-approved RA Consent - Applicant
#                    #
#                    if (sid005a_current_document_doctype.find("Pre-approved RA Consent") > -1):
#                        d["Suffix_Pre-Approved_RA_Consent_RA_Name"] = self.get_string(sid005a_dict, "sid005a_current_document_suffix")
                    
#                    # Proposed Plan (LV Modifications) - Applicant
#                    #
#                    if (sid005a_current_document_doctype.find("Proposed Plan (LV Modifications)") > -1):
#                        d["Suffix_Proposed_Plan_LV_Modifications_Surveyor_Plan_Version"] = self.get_suffix_surveyors_plan_version(sid005a_dict, "sid005a_current_document_suffix")
                    
#                    # Proposed Plan of Subdivision for Re-Certification - Applicant
#                    #
#                    if (sid005a_current_document_doctype.find("Proposed Plan of Subdivision for Re-Certification") > -1):
#                        d["Suffix_Proposed_Plan_of_Subdivision_for_Re-Certification_Surveyor_Plan_Version"] = self.get_suffix_surveyors_plan_version(sid005a_dict, "sid005a_current_document_suffix")
                    
#                    # RA Response - Council (on behalf of RA), RA
#                    # - Date is 1st-field (space-delimited) of suffix.
#                    # - Time is 2nd-field (space-delimited) of suffix. Must trim comma from end.
#                    # - Outcome is after the 1st-field (comma-delimited) of suffix.
#                    #
#                    if (sid005a_current_document_doctype == "RA Response"):
#                        d["Suffix_RA_Response_Referral_Date"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=0)
#                        d["Suffix_RA_Response_Referral_Time"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=1).replace(",", "")
#                        
#                        if (sid005a_dict["sid005a_current_document_suffix"].find("Section 55") > -1):
#                            d["Suffix_RA_Response_Section_55_Abridged_Response_Outcome"] = self.get_suffix_section55_outcome(sid005a_dict, "sid005a_current_document_suffix")
#                        
#                        if (sid005a_dict["sid005a_current_document_suffix"].find("Certification") > -1):
#                            d["Suffix_RA_Response_Section_8_Cert_Abridged_Response_Outcome"] = self.get_suffix_section8Cert_outcome(sid005a_dict, "sid005a_current_document_suffix")
#                        
#                        if (sid005a_dict["sid005a_current_document_suffix"].find("SOC") > -1):
#                            d["Suffix_RA_Response_Section_8_SOC_Abridged_Response_Outcome"] = self.get_suffix_section8SOC_outcome(sid005a_dict, "sid005a_current_document_suffix")
                    
#                    # Response to Request for Further Information - Applicant
#                    # - Subject is all fields after first comma of suffix.
#                    #
#                    if (sid005a_current_document_doctype.find("Response to Request for Further Information") > -1):
#                        d["Suffix_Response_to_Additional_Information_Request_Subject"] = self.get_string_multipart(sid005a_dict, "sid005a_current_document_suffix", separator=',', start_index=1, end_index=-1)
                    
#                     Site plan (proposed) - Applicant
#                    
#                    if (sid005a_current_document_doctype.find("Site plan (proposed)") > -1):
#                        d["Suffix_Site_Plan_Proposed_Number_of_Pages"] = self.get_suffix_number_of_pages(sid005a_dict, "sid005a_current_document_suffix")
                    
#                     Submission to Advertising Notice - RA
#                     - Section number is the last field (space-delimited) of suffix.
#                    
#                    if (sid005a_current_document_doctype == "Submission to Advertising Notice"):
#                        d["Suffix_Submission_to_Advertising_Notice_Section"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
                    
#                     Surveyor's Report - Applicant
#                    
#                    if (sid005a_current_document_doctype.find("Surveyor's Report") > -1):
#                        d["Suffix_Surveyors_Report_Number_of_Pages"] = self.get_suffix_number_of_pages(sid005a_dict, "sid005a_current_document_suffix")
                    
#                    d["Size"] = self.get_string(sid005a_dict, "sid005a_current_document_size")
#                    d["Format"] = self.get_string(sid005a_dict, "sid005a_current_document_format")
#                    d["Version_Number"] = self.get_string(sid005a_dict, "sid005a_current_document_version_number")
#                    d["Version_Description"] = self.get_string(sid005a_dict, "sid005a_current_document_version_description")
#                    d["Submitted_Date"] = self.get_string(sid005a_dict, "sid005a_current_document_submitted_date")
#                    d["Authenticated_By_First_Name"] = self.get_name_firstname(sid005a_dict, "sid005a_current_document_authenticated_by")
#                    d["Authenticated_By_Last_Name"] = self.get_name_lastname(sid005a_dict, "sid005a_current_document_authenticated_by", is_organisation=True)
#                    d["Authenticated_By_Organisation_Name"] = self.get_name_organisation(sid005a_dict, "sid005a_current_document_authenticated_by")

        # sid006a...
        #
        rs_application_dict["SPEAR_Reference_Number"] = self.get_string(sid006a_dict, "sid006a_spear_reference_number")
        rs_application_dict["Supplied_By_First_Name"] = self.get_name_firstname(sid006a_dict, "sid006a_supplied_by")
        rs_application_dict["Supplied_By_Last_Name"] = self.get_name_lastname(sid006a_dict, "sid006a_supplied_by", is_organisation=False)
        rs_application_dict["Submitted_Date"] = self.get_string(sid006a_dict, "sid006a_submitted_date")
        rs_application_dict["Modified_By_First_Name"] = self.get_name_firstname(sid006a_dict, "sid006a_modified_by")
        rs_application_dict["Modified_By_Last_Name"] = self.get_name_lastname(sid006a_dict, "sid006a_modified_by", is_organisation=True)
        rs_application_dict["Modified_By_Organisation_Name"] = self.get_name_organisation(sid006a_dict, "sid006a_modified_by")
        rs_application_dict["Modified_Date"] = self.get_string(sid006a_dict, "sid006a_modified_date")
        rs_application_dict["Application_Type"] = self.get_string(sid006a_dict, "sid006a_application_type")
        rs_application_dict["Version_Number"] = self.get_integer(sid006a_dict, "sid006a_version_number")
        rs_application_dict["Previous_SPEAR_Reference_Number"] = self.get_string(sid006a_dict, "sid006a_previous_spear_reference_number")
        rs_application_dict["Previous_Planning_Permit_Number"] = self.get_string(sid006a_dict, "sid006a_previous_planning_permit_number")
        rs_application_dict["Previous_Responsible_Authority_Reference_Number"] = self.get_string(sid006a_dict, "sid006a_previous_responsible_authority_reference_number")
        rs_application_dict["Previous_ApplicantContact_First_Name"] = self.get_name_firstname(sid006a_dict, "sid006a_previous_applicant_contact")
        rs_application_dict["Previous_ApplicantContact_Last_Name"] = self.get_name_lastname(sid006a_dict, "sid006a_previous_applicant_contact", is_organisation=False)
        rs_application_dict["Planning_Permit_Requirement"] = self.get_string(sid006a_dict, "sid006a_planning_permit_requirement")
        rs_application_dict["Council_Organisation_Name"] = self.get_string(sid006a_dict, "sid006a_council_org_name")
        rs_application_dict["Plan_Number"] = self.get_string(sid006a_dict, "sid006a_plan_number")
        rs_application_dict["ApplicantContact_Name_Title"] = self.get_name_title(sid006a_dict, "sid006a_applicant_contact_name")
        rs_application_dict["ApplicantContact_Name_First_Name"] = self.get_name_firstname(sid006a_dict, "sid006a_applicant_contact_name")
        rs_application_dict["ApplicantContact_Name_Last_Name"] = self.get_name_lastname(sid006a_dict, "sid006a_applicant_contact_name", is_organisation=False)
        rs_application_dict["ApplicantContact_Organisation_Name"] = self.get_string(sid006a_dict, "sid006a_applicant_contact_organisation_name")
        rs_application_dict["ApplicantContact_Address_Details_Unit_No"] = self.get_address_unit_no(sid006a_dict, "sid006a_applicant_contact_address_details")
        rs_application_dict["ApplicantContact_Address_Details_Street_No"] = self.get_address_street_no(sid006a_dict, "sid006a_applicant_contact_address_details")
        rs_application_dict["ApplicantContact_Address_Details_Street_Name"] = self.get_address_street_name(sid006a_dict, "sid006a_applicant_contact_address_details")
        rs_application_dict["ApplicantContact_Address_Details_Street_Type"] = self.get_address_street_type(sid006a_dict, "sid006a_applicant_contact_address_details")
        rs_application_dict["ApplicantContact_Address_Details_Suburb"] = self.get_address_suburb(sid006a_dict, "sid006a_applicant_contact_address_details")
        rs_application_dict["ApplicantContact_Address_Details_State"] = self.get_address_state(sid006a_dict, "sid006a_applicant_contact_address_details")
        rs_application_dict["ApplicantContact_Address_Details_Postcode"] = self.get_address_postcode(sid006a_dict, "sid006a_applicant_contact_address_details")
        rs_application_dict["ApplicantContact_Address_Details_Country"] = self.get_address_country(sid006a_dict, "sid006a_applicant_contact_address_details")
        rs_application_dict["ApplicantContact_Business_Phone_Area_Code"] = self.get_phone_area_code(sid006a_dict, "sid006a_applicant_contact_business_phone")
        rs_application_dict["ApplicantContact_Business_Phone_Number"] = self.get_phone_number(sid006a_dict, "sid006a_applicant_contact_business_phone")
        rs_application_dict["ApplicantContact_Email"] = self.get_string(sid006a_dict, "sid006a_applicant_contact_e-mail")
        
        
        if (sid006a_dict.has_key('sid006a_applicant_name') and sid006a_dict["sid006a_applicant_name"] == "(Applicant details per Applicant Contact)"):
            rs_application_dict["Applicant_Organisation_Name"] = self.get_string(sid006a_dict, "sid006a_applicant_contact_organisation_name")
            
            rs_application_dict["Applicant_Name_Title"] = self.get_name_title(sid006a_dict, "sid006a_applicant_contact_name")
            rs_application_dict["Applicant_Name_First_Name"] = self.get_name_firstname(sid006a_dict, "sid006a_applicant_contact_name")
            rs_application_dict["Applicant_Name_Last_Name"] = self.get_name_lastname(sid006a_dict, "sid006a_applicant_contact_name", is_organisation=False)
            
            rs_application_dict["Applicant_Address_Details_Unit_No"] = self.get_address_unit_no(sid006a_dict, "sid006a_applicant_contact_address_details")
            rs_application_dict["Applicant_Address_Details_Street_No"] = self.get_address_street_no(sid006a_dict, "sid006a_applicant_contact_address_details")
            rs_application_dict["Applicant_Address_Details_Street_Name"] = self.get_address_street_name(sid006a_dict, "sid006a_applicant_contact_address_details")
            rs_application_dict["Applicant_Address_Details_Street_Type"] = self.get_address_street_type(sid006a_dict, "sid006a_applicant_contact_address_details")
            rs_application_dict["Applicant_Address_Details_Suburb"] = self.get_address_suburb(sid006a_dict, "sid006a_applicant_contact_address_details")
            rs_application_dict["Applicant_Address_Details_State"] = self.get_address_state(sid006a_dict, "sid006a_applicant_contact_address_details")
            rs_application_dict["Applicant_Address_Details_Postcode"] = self.get_address_postcode(sid006a_dict, "sid006a_applicant_contact_address_details")
            rs_application_dict["Applicant_Address_Details_Country"] = self.get_address_country(sid006a_dict, "sid006a_applicant_contact_address_details")
        else:
            rs_application_dict["Applicant_Organisation_Name"] = self.get_string(sid006a_dict, "sid006a_applicant_organisation_name")
            
            rs_application_dict["Applicant_Name_Title"] = self.get_name_title(sid006a_dict, "sid006a_applicant_name")
            rs_application_dict["Applicant_Name_First_Name"] = self.get_name_firstname(sid006a_dict, "sid006a_applicant_name")
            rs_application_dict["Applicant_Name_Last_Name"] = self.get_name_lastname(sid006a_dict, "sid006a_applicant_name", is_organisation=False)

            rs_application_dict["Applicant_Address_Details_Unit_No"] = self.get_address_unit_no(sid006a_dict, "sid006a_applicant_address_details")
            rs_application_dict["Applicant_Address_Details_Street_No"] = self.get_address_street_no(sid006a_dict, "sid006a_applicant_address_details")
            rs_application_dict["Applicant_Address_Details_Street_Name"] = self.get_address_street_name(sid006a_dict, "sid006a_applicant_address_details")
            rs_application_dict["Applicant_Address_Details_Street_Type"] = self.get_address_street_type(sid006a_dict, "sid006a_applicant_address_details")
            rs_application_dict["Applicant_Address_Details_Suburb"] = self.get_address_suburb(sid006a_dict, "sid006a_applicant_address_details")
            rs_application_dict["Applicant_Address_Details_State"] = self.get_address_state(sid006a_dict, "sid006a_applicant_address_details")
            rs_application_dict["Applicant_Address_Details_Postcode"] = self.get_address_postcode(sid006a_dict, "sid006a_applicant_address_details")
            rs_application_dict["Applicant_Address_Details_Country"] = self.get_address_country(sid006a_dict, "sid006a_applicant_address_details")
        
        if  sid006a_dict.has_key("sid006a_proposal_type") and sid006a_dict["sid006a_proposal_type"] != '' :
            rs_application_dict["Proposal_Type_Subdivision_Dealing_Type_Description"] = ""
            rs_application_dict["Proposal_Type_Subdivision_No_of_Lots"] = 0
            rs_application_dict["Proposal_Type_Subdivision_Stage_Number"] = ""
            rs_application_dict["Proposal_Type_Trigger_List"] = []
            
            if (sid006a_dict["sid006a_proposal_type"][0].startswith("Planning permit") is True):
                rs_application_dict["Application_Classification"] = "Planning"
                rs_application_dict["Proposal_Type_Trigger_List"] = self.get_proposal_type_trigger_tiers_list(sid006a_dict, "sid006a_proposal_type")
            else:
                rs_application_dict["Application_Classification"] = "Subdivision"
                if (sid006a_dict["sid006a_proposal_type"][0].startswith("Dealing Type")):
                    rs_application_dict["Proposal_Type_Subdivision_Dealing_Type_Description"] = self.get_proposal_type_dealingtype_description(sid006a_dict, "sid006a_proposal_type")
                if (sid006a_dict["sid006a_proposal_type"][0].find("lots") > -1):
                    rs_application_dict["Proposal_Type_Subdivision_No_of_Lots"] = self.get_proposal_type_subdivision_no_of_lots(sid006a_dict, "sid006a_proposal_type")
                if (sid006a_dict["sid006a_proposal_type"][0].find("Stage") > -1):
                    rs_application_dict["Proposal_Type_Subdivision_Stage_Number"] = self.get_proposal_type_subdivision_stage_number(sid006a_dict, "sid006a_proposal_type")
        
        rs_application_dict["Proposed_Use"] = self.get_string(sid006a_dict, "sid006a_proposed_use")
        rs_application_dict["Current_Use"] = self.get_string(sid006a_dict, "sid006a_current_use")
        rs_application_dict["Estimated_Development_Cost"] = self.get_string(sid006a_dict, "sid006a_estimated_development_cost")
        rs_application_dict["Covenant_Declaration"] = self.get_string(sid006a_dict, "sid006a_covenant_declaration")
        rs_application_dict["Covenant_Declaration_Comment"] = self.get_string(sid006a_dict, "sid006a_covenant_declaration_comment")
        rs_application_dict["CHMP_Declaration"] = self.get_string(sid006a_dict, "sid006a_chmp_declaration")
        rs_application_dict["Authorised_By_First_Name"] = self.get_name_firstname(sid006a_dict, "sid006a_authorised_by")
        rs_application_dict["Authorised_By_Last_Name"] = self.get_name_lastname(sid006a_dict, "sid006a_authorised_by", is_organisation=False)
        rs_application_dict["Authorised_Applicant_Organisation_Name"] = self.get_string(sid006a_dict, "sid006a_applicant_organisation_name")
        
        rs_application_dict["Owner"] = []
        for i in range(0, self.get_listlength(sid006a_dict, "sid006a_owner_dict_list")):
            rs_application_dict["Owner"].append({})
            
            if (sid006a_dict["sid006a_owner_dict_list"][i]["sid006a_owner_name"] == "(Owner details per Applicant)"):
                rs_application_dict["Owner"][-1]["Organisation_Name"] = self.get_string(sid006a_dict, "sid006a_applicant_organisation_name")
                
                rs_application_dict["Owner"][-1]["Name_Title"] = self.get_name_title(sid006a_dict, "sid006a_applicant_name")
                rs_application_dict["Owner"][-1]["Name_First_Name"] = self.get_name_firstname(sid006a_dict, "sid006a_applicant_name")
                rs_application_dict["Owner"][-1]["Name_Last_Name"] = self.get_name_lastname(sid006a_dict, "sid006a_applicant_name", is_organisation=False)
                
                rs_application_dict["Owner"][-1]["Address_Details_Unit_No"] = self.get_address_unit_no(sid006a_dict, "sid006a_applicant_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_Street_No"] = self.get_address_street_no(sid006a_dict, "sid006a_applicant_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_Street_Name"] = self.get_address_street_name(sid006a_dict, "sid006a_applicant_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_Street_Type"] = self.get_address_street_type(sid006a_dict, "sid006a_applicant_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_Suburb"] = self.get_address_suburb(sid006a_dict, "sid006a_applicant_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_State"] = self.get_address_state(sid006a_dict, "sid006a_applicant_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_Postcode"] = self.get_address_postcode(sid006a_dict, "sid006a_applicant_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_Country"] = self.get_address_country(sid006a_dict, "sid006a_applicant_address_details")
                
            elif (sid006a_dict["sid006a_owner_dict_list"][i]["sid006a_owner_name"] == "(Owner details per Applicant Contact)"):
                rs_application_dict["Owner"][-1]["Organisation_Name"] = self.get_string(sid006a_dict, "sid006a_applicant_contact_organisation_name")
                
                rs_application_dict["Owner"][-1]["Name_Title"] = self.get_name_title(sid006a_dict, "sid006a_applicant_contact_name")
                rs_application_dict["Owner"][-1]["Name_First_Name"] = self.get_name_firstname(sid006a_dict, "sid006a_applicant_contact_name")
                rs_application_dict["Owner"][-1]["Name_Last_Name"] = self.get_name_lastname(sid006a_dict, "sid006a_applicant_contact_name", is_organisation=False)
                
                rs_application_dict["Owner"][-1]["Address_Details_Unit_No"] = self.get_address_unit_no(sid006a_dict, "sid006a_applicant_contact_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_Street_No"] = self.get_address_street_no(sid006a_dict, "sid006a_applicant_contact_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_Street_Name"] = self.get_address_street_name(sid006a_dict, "sid006a_applicant_contact_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_Street_Type"] = self.get_address_street_type(sid006a_dict, "sid006a_applicant_contact_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_Suburb"] = self.get_address_suburb(sid006a_dict, "sid006a_applicant_contact_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_State"] = self.get_address_state(sid006a_dict, "sid006a_applicant_contact_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_Postcode"] = self.get_address_postcode(sid006a_dict, "sid006a_applicant_contact_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_Country"] = self.get_address_country(sid006a_dict, "sid006a_applicant_contact_address_details")
                
            else:
                rs_application_dict["Owner"][-1]["Organisation_Name"] = self.get_string(sid006a_dict, "sid006a_owner_organisation_name")
                
                rs_application_dict["Owner"][-1]["Name_Title"] = self.get_name_title(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_name")
                rs_application_dict["Owner"][-1]["Name_First_Name"] = self.get_name_firstname(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_name")
                rs_application_dict["Owner"][-1]["Name_Last_Name"] = self.get_name_lastname(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_name", is_organisation=False)
                
                rs_application_dict["Owner"][-1]["Address_Details_Unit_No"] = self.get_address_unit_no(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_Street_No"] = self.get_address_street_no(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_Street_Name"] = self.get_address_street_name(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_Street_Type"] = self.get_address_street_type(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_Suburb"] = self.get_address_suburb(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_State"] = self.get_address_state(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_Postcode"] = self.get_address_postcode(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_address_details")
                rs_application_dict["Owner"][-1]["Address_Details_Country"] = self.get_address_country(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_address_details")
            
        rs_application_dict["Property"] = []
        for i in range(0, self.get_listlength(sid006a_dict, "sid006a_property_dict_list")):
            rs_application_dict["Property"].append({})
            
            rs_application_dict["Property"][-1]["Address_Unit_No"] = self.get_address_unit_no(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_address")
            rs_application_dict["Property"][-1]["Address_Street_No"] = self.get_address_street_no(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_address")
            rs_application_dict["Property"][-1]["Address_Street_Name"] = self.get_address_street_name(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_address")
            rs_application_dict["Property"][-1]["Address_Street_Type"] = self.get_address_street_type(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_address")
            rs_application_dict["Property"][-1]["Address_Street_Suffix"] = self.get_address_street_suffix(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_address")
            rs_application_dict["Property"][-1]["Address_Suburb"] = self.get_address_suburb(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_address")
            rs_application_dict["Property"][-1]["Address_State"] = self.get_address_state(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_address")
            rs_application_dict["Property"][-1]["Address_Postcode"] = self.get_address_postcode(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_address")
            rs_application_dict["Property"][-1]["Address_Country"] = self.get_address_country(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_address")
            rs_application_dict["Property"][-1]["CPN"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_cpn")
            rs_application_dict["Property"][-1]["Lot_Number"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_lot_number")
            rs_application_dict["Property"][-1]["Plan_Number"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_plan_number")
            rs_application_dict["Property"][-1]["No_VolFol"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_no_volfol")
            rs_application_dict["Property"][-1]["Volume_Number"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_volume_number")
            rs_application_dict["Property"][-1]["Folio_Number"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_folio_number")
            rs_application_dict["Property"][-1]["Crown_Allotment_No"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_crown_allotment_no")
            rs_application_dict["Property"][-1]["Section_No"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_section_no")
            rs_application_dict["Property"][-1]["Parish_Name"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_parish_name")
            rs_application_dict["Property"][-1]["SPI"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_spi")
            rs_application_dict["Property"][-1]["Other"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_other")
        
        # sid061a...
        #
        for ra_request_document_id, sid061a_dict in sid061a_dict_dict.iteritems():
            for ra_dict in sid061a_dict["sid061a_ra_dict_dict"].itervalues():
                d = {}
                
                # sid061a_date format - dd/mm/YYYY
                # sid061a_time format - HH:MM
                # Output date/time format - YYYY-mm-ddTHH:MM:SS
                #
                try:
                    dt_date = datetime.strptime(self.get_string(sid061a_dict, "sid061a_date"), "%d/%m/%Y")
                    dt_time = datetime.strptime(self.get_string(sid061a_dict, "sid061a_time"), "%Y-%m-%d %H:%M:%S.%f")
                    dt = datetime.combine(dt_date.date(), dt_time.time())
                    d["DateTime"] = dt.strftime("%Y-%m-%dT%H:%M:%S")
                except ValueError:
                    d["DateTime"] = ""
                    
                d["Council_RA_Comments"] = self.get_string(ra_dict, "sid061a_council_ra_comments")
                d["RA_Name"] = self.get_string(ra_dict, "sid061a_ra_name")
                d["RA_Requirement"] = self.get_string(ra_dict, "sid061a_ra_requirements")
                d["ReferralRequest_Supplied_By"] = self.get_string(sid061a_dict, "sid061a_supplied_by")
                
                d["referral_request_document_id"] = ra_request_document_id
                d["referral_response_document_id_list"] = []
                
                # Find RA responses for this request...
                #
                # Note: A "Section 55 & 8" referral request can have separate 
                #       responses from each RA. Therefore we must store a list
                #       of response ids per request.
                #
                
                for ra_response_document_id, sid005a_dict in sid005a_dict_dict.iteritems():
                    if ("sid005a_ra_request_document_id_list" in sid005a_dict) and \
                       (ra_request_document_id in sid005a_dict["sid005a_ra_request_document_id_list"]):
                        d["referral_response_document_id_list"].append(ra_response_document_id)
                
                rs_referrals_dict_list.append(d)
                
#        # sid061a...
#        #
#        for ra_request_document_id, sid061a_dict in sid061a_dict_dict.iteritems():
#            for ra_dict in sid061a_dict["sid061a_ra_dict_dict"].itervalues():
#                d = {}
#                d["ReferralRequest_Date"] = self.get_string(sid061a_dict, "sid061a_date")
#                d["ReferralRequest_Time"] = self.get_string(sid061a_dict, "sid061a_time")
#                d["ReferralRequest_Supplied_By"] = self.get_string(sid061a_dict, "sid061a_supplied_by")
#                d["RA_Name"] = self.get_string(ra_dict, "sid061a_ra_name")
#                d["RA_Requirements_Section_55_Flag"] = (self.get_string(ra_dict, "sid061a_ra_requirements") == "Section 55")
#                d["RA_Requirements_Section_8_Flag"] = (self.get_string(ra_dict, "sid061a_ra_requirements") == "Section 8")
#                d["RA_Requirements_Section_55_8_Flag"] = (self.get_string(ra_dict, "sid061a_ra_requirements") == "Section 8 &amp; Section 55")
#                d["RA_Requirements_For_Info_Flag"] = (self.get_string(ra_dict, "sid061a_ra_requirements") == "For information only")
#                d["Council_RA_Comments"] = self.get_string(ra_dict, "sid061a_council_ra_comments")
#                d["referral_request_document_id"] = ra_request_document_id
#                d["referral_response_document_id_list"] = []
#                
#                # Find RA responses for this request...
#                #
#                # Note: A "Section 55 & 8" referral request can have separate 
#                #       responses from each RA. Therefore we must store a list
#                #       of response ids per request.
#                #
#                
#                for ra_response_document_id, sid005a_dict in sid005a_dict_dict.iteritems():
#                    if ("sid005a_ra_request_document_id_list" in sid005a_dict) and \
#                       (ra_request_document_id in sid005a_dict["sid005a_ra_request_document_id_list"]):
#                        d["referral_response_document_id_list"].append(ra_response_document_id)
#                
#                rs_referrals_dict_list.append(d)
            
        # sid072a...
        #
        rs_application_dict["Reference_Number_1"] = self.get_string(sid072a_dict, "sid072a_reference_number_1")
        rs_application_dict["Reference_Number_2"] = self.get_string(sid072a_dict, "sid072a_reference_number_2")
        rs_application_dict["Delegate_Planning_Officer_First_Name"] = self.get_name_firstname(sid072a_dict, "sid072a_delegate_planning_officer_full_name")
        rs_application_dict["Delegate_Planning_Officer_Last_Name"] = self.get_name_lastname(sid072a_dict, "sid072a_delegate_planning_officer_full_name", is_organisation=False)
        
        app_rs.set_var("application", rs_application_dict)
        app_rs.set_var("referrals", rs_referrals_dict_list)
        app_rs.set_var("attachments", rs_attachments_dict)
        app_rs.set_var("mandatory_action_list", rs_mandatory_action_list)
        app_rs.set_var("optional_action_list", rs_optional_action_list)
        
#    def do_derivations(self, app_rs):
#        """
#        Derive the data required by eDAIS from the Spear application data stored
#        in the argument Resource 'app_rs'. The derivations are stored in 'app_rs' 
#        according to the Spear/eDAIS mapping spreadsheet.
#        
#        :param app_rs: Resource - the Spear application data.
#        """
#        
#        rs_application_dict = {}
#        rs_referrals_dict_list = []
#        rs_attachments_dict = {}
#        rs_mandatory_action_list = []
#        rs_optional_action_list = []
#        
#        sid002b_dict = {}
#        if (app_rs.has_var("sid002b_dict")):
#            sid002b_dict = app_rs.get_var("sid002b_dict")
#        
#        sid004_dict = {}
#        sid004_attachment_dict_list = []
#        if (app_rs.has_var("sid004_dict")):
#            sid004_dict = app_rs.get_var("sid004_dict")
#            sid004_attachment_dict_list = self.get_list(sid004_dict, "sid004_attachment_dict_list")
#            
#        sid005a_dict_dict = {}
#        if (app_rs.has_var("sid005a_dict_dict")):
#            sid005a_dict_dict = app_rs.get_var("sid005a_dict_dict")
#        
#        sid006a_dict = {}
#        if (app_rs.has_var("sid006a_dict")):
#            sid006a_dict = app_rs.get_var("sid006a_dict")
#        
#        sid061a_dict_dict = []
#        if (app_rs.has_var("sid061a_dict_dict")):
#            sid061a_dict_dict = app_rs.get_var("sid061a_dict_dict")
#        
#        sid072a_dict = {}
#        if (app_rs.has_var("sid072a_dict")):
#            sid072a_dict = app_rs.get_var("sid072a_dict")
#        
#        sid075c_dict_dict = []
#        if (app_rs.has_var("sid075c_dict_dict")):
#            sid075c_dict_dict = app_rs.get_var("sid075c_dict_dict")
#        
#        # We are pulling applications from the Spear 'current' applications list. 
#        # Because they are current they are NOT complete.
#        #
#        app_rs.set_var("Completed_Flag", False)
#        
#        # sid002b...
#        #
#        rs_application_dict["Application_Status"] = self.get_string(sid002b_dict, "sid002b_status")
#        
#        # sid004...
#        #
#        rs_mandatory_action_list = self.get_list(sid004_dict, "sid004_current_actors_mandatory_action_list")
#        rs_optional_action_list = self.get_list(sid004_dict, "sid004_optional_action_list")
#        
#        for attachment_dict in sid004_attachment_dict_list:
#            # We add to rs_attachments_dict only those documents that are in sid005a_dict_dict,
#            # that is, those application documents that clickthru to the document meta-data page.
#            #
#            # rs_attachments_dict is a dictionary, keyed by document-type, where each element
#            # is a dictionary (of dictionaries). Each element dictionary is keyed by the 
#            # attachment's document-id. 
#            #
#            
#            sid004_doc_id = attachment_dict["sid004_doc_id"]
#            
#            # Find the meta-data (sid005a) for this attachment...
#            #
#            sid005a_dict = {}
#            for attachment_document_id, sid005a_dict in sid005a_dict_dict.iteritems():
#                if (attachment_document_id == sid004_doc_id):
#                    d = {}
#                    if (attachment_dict["sid004_doctype"] not in rs_attachments_dict):
#                        rs_attachments_dict[attachment_dict["sid004_doctype"]] = {}
#                    rs_attachments_dict[attachment_dict["sid004_doctype"]][sid004_doc_id] = d
#                    
#                    d["Owner_Type"] = attachment_dict["sid004_owner_type__derived"]
#                    
#                    if (attachment_dict["sid004_owner_type__derived"] == "Applicant Contact") and \
#                       ("sid006a_applicant_contact_organisation_name" in sid006a_dict):
#                        if (self.get_stringlength(sid006a_dict, "sid006a_applicant_contact_organisation_name") > 0):
#                            d["Owner_Organisation_Name"] = self.get_string(attachment_dict, "sid004_owner__derived")
#                            d["Owner_ApplicantContact_First_Name"] = ""
#                            d["Owner_ApplicantContact_Last_Name"] = ""
#                        else:
#                            d["Owner_Organisation_Name"] = ""
#                            d["Owner_ApplicantContact_First_Name"] = self.get_name_firstname(attachment_dict, "sid004_owner__derived")
#                            d["Owner_ApplicantContact_Last_Name"] = self.get_name_lastname(attachment_dict, "sid004_owner__derived", is_organisation=False)
#                    
#                    if (attachment_dict["sid004_owner_type__derived"] == "Responsible Authority"):
#                        d["Owner_Organisation_Name"] = self.get_string(attachment_dict, "sid004_owner__derived")
#                        d["Owner_ApplicantContact_First_Name"] = ""
#                        d["Owner_ApplicantContact_Last_Name"] = ""
#                    
#                    if (attachment_dict["sid004_owner_type__derived"] == "Referral Authority") or \
#                       (attachment_dict["sid004_owner_type__derived"] == "Online Objector") or \
#                       (attachment_dict["sid004_owner_type__derived"] == "VCAT"):
#                        d["Owner_Organisation_Name"] = ""
#                        d["Owner_ApplicantContact_First_Name"] = ""
#                        d["Owner_ApplicantContact_Last_Name"] = ""
#                    
#                    d["Objection_Number"] = self.get_string(attachment_dict, "sid004_objection_number")
#                    d["Objection_Withdrawn"] = self.get_string(attachment_dict, "sid004_objection_withdrawn")
#                    
#                    # relate any RA-comments to a referral response.
#                    #
#                    if (attachment_dict["sid004_doctype"] == "RA Response"):
#                        for ra_response_document_id, sid075c_dict in sid075c_dict_dict.iteritems():
#                            if (ra_response_document_id == sid004_doc_id):
#                                d["RA_Comments"] = sid075c_dict["sid075c_comments"]
#                    
#                    # sid005a...
#                    #
#                    sid005a_current_document_doctype = sid005a_dict["sid005a_current_document_doctype"]
#                    
#                    # Note: When a planning permit is being issued applicant attachments are endorsed. 
#                    #       The 'doctype' of some of these attachments goes from "<doctype>" to 
#                    #       "Endorsed <doctype>". 
#                    #
#                    # Note: Some sid005a_dicts will have empty "document_url"s (eg. Application 
#                    #       Acceptance/Rejection).
#                    #
#                    
#                    # Objection - Objectors
#                    #
#                    if (sid005a_current_document_doctype == "Objection"):
#                        d["Online_Objector_First_Name"] = self.get_name_firstname(sid005a_dict, "sid005a_current_document_authenticated_by")
#                        d["Online_Objector_Last_Name"] = self.get_name_lastname(sid005a_dict, "sid005a_current_document_authenticated_by", is_organisation=False)
#                    
#                    d["URL"] = ""
#                    d["Prefix"] = self.get_string(sid005a_dict, "sid005a_current_document_prefix")
#                    d["doc_type"] = self.get_string(sid005a_dict, "sid005a_current_document_doctype")
#                    d["Suffix"] = self.get_string(sid005a_dict, "sid005a_current_document_suffix")
#                    
#                    # Application Acceptance / Rejection - Council
#                    # - Outcome is whole suffix.
#                    #
#                    if (sid005a_current_document_doctype == "Application Acceptance / Rejection"):
#                        d["Suffix_Accept_Reject_Outcome"] = self.get_string(sid005a_dict, "sid005a_current_document_suffix")
#                    
#                    # Request for Further Information - Council, RA
#                    # - Subject is 2nd-field (comma-delimited) of suffix.
#                    # - Due date is last field (space-delimited) of suffix.
#                    #
#                    if (sid005a_current_document_doctype == "Request for Further Information"):
#                        d["Suffix_Additional_Information_Requested_Subject"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=1)
#                        d["Suffix_Additional_Information_Requested_Due_Date"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
#                    
#                    # Advertising Instructions - Council
#                    # - Section number is the last field (space-delimited) of suffix.
#                    # - Outcome is 2nd-field (comma-delimited) of suffix.
#                    #
#                    if (sid005a_current_document_doctype == "Advertising Instructions"):
#                        d["Suffix_Advertising_Instructions_Section"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
#                        d["Suffix_Advertising_Instructions_Outcome"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=1)
#                    
#                    # Advertising Notice to Referral Authorities - Council
#                    # - Section number is the last field (space-delimited) of suffix.
#                    #
#                    if (sid005a_current_document_doctype == "Advertising Notice to Referral Authorities"):
#                        d["Suffix_Advertising_Notice_to_Referral_Authorities_Section"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
#                    
#                    # Arborist report - Applicant
#                    # - Number of pages is last field (space-delimited) of suffix.
#                    #
#                    if (sid005a_current_document_doctype.find("Arborist report") > -1):
#                        d["Suffix_Arborist_Report_Number_of_Pages"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
#                    
#                    # Compiled Plan Sheets - Applicant
#                    # - Number of pages is last field (space-delimited) of suffix.
#                    #
#                    if (sid005a_current_document_doctype.find("Compiled Plan Sheets") > -1):
#                        d["Suffix_Compiled_Plan_Sheets_Number_of_Pages"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
#                    
#                    # Copy of Title - Applicant
#                    # - Vol/Fol are all but 1st-field (space-delimited) of suffix.
#                    #
#                    if (sid005a_current_document_doctype.find("Copy of Title") > -1):
#                        d["Suffix_Copy_of_Title_VolFol"] = self.get_string_multipart(sid005a_dict, "sid005a_current_document_suffix", separator=' ', start_index=1, end_index=-1)
#                    
#                    # Decision - Amended Planning Permit - Council
#                    # - Outcome is 2nd field (comma-delimited) of suffix.
#                    # - Required Action for Endorsement is 3rd field (comma-delimited) of suffix, if present.
#                    #
#                    if (sid005a_current_document_doctype.find("Decision - Amended Planning Permit") > -1):
#                        d["Suffix_Decision_Amended_Planning_Permit_Outcome"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=1)
#                        d["Suffix_Decision_Amended_Planning_Permit_Required_Action_for_Endorsement"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=2)
#                        
#                    # Decision - Planning Permit - Council
#                    # - Outcome is 2nd field (comma-delimited) of suffix.
#                    # - Required Action for Endorsement is 3rd field (comma-delimited) of suffix, if present.
#                    #
#                    if (sid005a_current_document_doctype == "Decision - Planning Permit"):
#                        d["Suffix_Decision_Planning_Permit_Outcome"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=1)
#                        d["Suffix_Decision_Planning_Permit_Required_Action_for_Endorsement"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=2)
#                    
#                    # Extension of Time - Applicant
#                    # - Subject is the 2nd-fields (comma-delimited) of suffix.
#                    # - Number of days is the last field (space-delimited) of suffix.
#                    #
#                    if (sid005a_current_document_doctype.find("Extension of Time") > -1):
#                        d["Suffix_Extension_of_Time_Subject"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=1)
#                        d["Suffix_Extension_of_Time_Number_of_Days"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
#                    
#                    # Other Document Type - Applicant, Council, RA
#                    # - Whole of suffix.
#                    #
#                    if (sid005a_current_document_doctype.find("Other Document Type") > -1):
#                        d["Suffix_Other_Document_Type_Doctype_Description"] = self.get_string(sid005a_dict, "sid005a_current_document_suffix")
#                    
#                    # Plan of Demolition - Applicant
#                    # - Number of pages is last field (space-delimited) of suffix.
#                    # 
#                    if (sid005a_current_document_doctype.find("Plan of Demolition") > -1):
#                        d["Suffix_Plan_of_Demolition_Number_of_Pages"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
#                    
#                    # Plan for Endorsement / Endorsed Plan - Applicant
#                    # - Surveyor's Plan Version is the last field (space-delimited) of suffix.
#                    #
#                    if (sid005a_current_document_doctype == "Endorsed Plan") or \
#                       (sid005a_current_document_doctype == "Plan for Endorsement"):
#                        d["Suffix_Plan_for_Endorsement_Surveyor_Plan_Version"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
#                    
#                    # Plan of Subdivision or Consolidation - Applicant
#                    # - Number of pages is 4th-field (space-delimited) of suffix. Must trim comma from end.
#                    # - Surveyor's Plan Version is last field (space-delimited) of suffix. 
#                    #
#                    if (sid005a_current_document_doctype.find("Plan of Subdivision or Consolidation") > -1):
#                        d["Suffix_Plan_of_Subdivision_or_Consolidation_Number_of_Pages"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=3).replace(",", "")
#                        d["Suffix_Plan_of_Subdivision_or_Consolidation_Surveyor_Plan_Version"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
#                        
#                        if (sid005a_dict["sid005a_current_document_suffix"].find("modified at LV request") > -1):
#                            d["Suffix_Plan_of_Subdivision_or_Consolidation_Modified_LV_Request"] = "modified at LV request"
#                    
#                    # Pre-approved RA Consent - Applicant
#                    #
#                    if (sid005a_current_document_doctype.find("Pre-approved RA Consent") > -1):
#                        d["Suffix_Pre-Approved_RA_Consent_RA_Name"] = self.get_string(sid005a_dict, "sid005a_current_document_suffix")
#                    
#                    # Proposed Plan (LV Modifications) - Applicant
#                    #
#                    if (sid005a_current_document_doctype.find("Proposed Plan (LV Modifications)") > -1):
#                        d["Suffix_Proposed_Plan_LV_Modifications_Surveyor_Plan_Version"] = self.get_suffix_surveyors_plan_version(sid005a_dict, "sid005a_current_document_suffix")
#                    
#                    # Proposed Plan of Subdivision for Re-Certification - Applicant
#                    #
#                    if (sid005a_current_document_doctype.find("Proposed Plan of Subdivision for Re-Certification") > -1):
#                        d["Suffix_Proposed_Plan_of_Subdivision_for_Re-Certification_Surveyor_Plan_Version"] = self.get_suffix_surveyors_plan_version(sid005a_dict, "sid005a_current_document_suffix")
#                    
#                    # RA Response - Council (on behalf of RA), RA
#                    # - Date is 1st-field (space-delimited) of suffix.
#                    # - Time is 2nd-field (space-delimited) of suffix. Must trim comma from end.
#                    # - Outcome is after the 1st-field (comma-delimited) of suffix.
#                    #
#                    if (sid005a_current_document_doctype == "RA Response"):
#                        d["Suffix_RA_Response_Referral_Date"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=0)
#                        d["Suffix_RA_Response_Referral_Time"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=1).replace(",", "")
#                        
#                        if (sid005a_dict["sid005a_current_document_suffix"].find("Section 55") > -1):
#                            d["Suffix_RA_Response_Section_55_Abridged_Response_Outcome"] = self.get_suffix_section55_outcome(sid005a_dict, "sid005a_current_document_suffix")
#                        
#                        if (sid005a_dict["sid005a_current_document_suffix"].find("Certification") > -1):
#                            d["Suffix_RA_Response_Section_8_Cert_Abridged_Response_Outcome"] = self.get_suffix_section8Cert_outcome(sid005a_dict, "sid005a_current_document_suffix")
#                        
#                        if (sid005a_dict["sid005a_current_document_suffix"].find("SOC") > -1):
#                            d["Suffix_RA_Response_Section_8_SOC_Abridged_Response_Outcome"] = self.get_suffix_section8SOC_outcome(sid005a_dict, "sid005a_current_document_suffix")
#                    
#                    # Response to Request for Further Information - Applicant
#                    # - Subject is all fields after first comma of suffix.
#                    #
#                    if (sid005a_current_document_doctype.find("Response to Request for Further Information") > -1):
#                        d["Suffix_Response_to_Additional_Information_Request_Subject"] = self.get_string_multipart(sid005a_dict, "sid005a_current_document_suffix", separator=',', start_index=1, end_index=-1)
#                    
#                    # Site plan (proposed) - Applicant
#                    #
#                    if (sid005a_current_document_doctype.find("Site plan (proposed)") > -1):
#                        d["Suffix_Site_Plan_Proposed_Number_of_Pages"] = self.get_suffix_number_of_pages(sid005a_dict, "sid005a_current_document_suffix")
#                    
#                    # Submission to Advertising Notice - RA
#                    # - Section number is the last field (space-delimited) of suffix.
#                    #
#                    if (sid005a_current_document_doctype == "Submission to Advertising Notice"):
#                        d["Suffix_Submission_to_Advertising_Notice_Section"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
#                    
#                    # Surveyor's Report - Applicant
#                    #
#                    if (sid005a_current_document_doctype.find("Surveyor's Report") > -1):
#                        d["Suffix_Surveyors_Report_Number_of_Pages"] = self.get_suffix_number_of_pages(sid005a_dict, "sid005a_current_document_suffix")
#                    
#                    d["Size"] = self.get_string(sid005a_dict, "sid005a_current_document_size")
#                    d["Format"] = self.get_string(sid005a_dict, "sid005a_current_document_format")
#                    d["Version_Number"] = self.get_string(sid005a_dict, "sid005a_current_document_version_number")
#                    d["Version_Description"] = self.get_string(sid005a_dict, "sid005a_current_document_version_description")
#                    d["Submitted_Date"] = self.get_string(sid005a_dict, "sid005a_current_document_submitted_date")
#                    d["Authenticated_By_First_Name"] = self.get_name_firstname(sid005a_dict, "sid005a_current_document_authenticated_by")
#                    d["Authenticated_By_Last_Name"] = self.get_name_lastname(sid005a_dict, "sid005a_current_document_authenticated_by", is_organisation=True)
#                    d["Authenticated_By_Organisation_Name"] = self.get_name_organisation(sid005a_dict, "sid005a_current_document_authenticated_by")
#
##            # Find the meta-data (sid005a) for this attachment...
##            #
##            sid005a_dict = {}
##            for attachment_document_id, sid005a_dict in sid005a_dict_dict.iteritems():
##                if (attachment_document_id == sid004_doc_id):
##                    rs_attachments_dict[sid004_doc_id] = {}
##                    rs_attachments_dict[sid004_doc_id]["Owner_Type"] = attachment_dict["sid004_owner_type__derived"]
##                    
##                    if (attachment_dict["sid004_owner_type__derived"] == "Applicant Contact") and \
##                       ("sid006a_applicant_contact_organisation_name" in sid006a_dict):
##                        if (self.get_stringlength(sid006a_dict, "sid006a_applicant_contact_organisation_name") > 0):
##                            rs_attachments_dict[sid004_doc_id]["Owner_Organisation_Name"] = self.get_string(attachment_dict, "sid004_owner__derived")
##                            rs_attachments_dict[sid004_doc_id]["Owner_ApplicantContact_First_Name"] = ""
##                            rs_attachments_dict[sid004_doc_id]["Owner_ApplicantContact_Last_Name"] = ""
##                        else:
##                            rs_attachments_dict[sid004_doc_id]["Owner_Organisation_Name"] = ""
##                            rs_attachments_dict[sid004_doc_id]["Owner_ApplicantContact_First_Name"] = self.get_name_firstname(attachment_dict, "sid004_owner__derived")
##                            rs_attachments_dict[sid004_doc_id]["Owner_ApplicantContact_Last_Name"] = self.get_name_lastname(attachment_dict, "sid004_owner__derived", is_organisation=False)
##                    
##                    if (attachment_dict["sid004_owner_type__derived"] == "Responsible Authority"):
##                        rs_attachments_dict[sid004_doc_id]["Owner_Organisation_Name"] = self.get_string(attachment_dict, "sid004_owner__derived")
##                        rs_attachments_dict[sid004_doc_id]["Owner_ApplicantContact_First_Name"] = ""
##                        rs_attachments_dict[sid004_doc_id]["Owner_ApplicantContact_Last_Name"] = ""
##                    
##                    if (attachment_dict["sid004_owner_type__derived"] == "Referral Authority") or \
##                       (attachment_dict["sid004_owner_type__derived"] == "Online Objector") or \
##                       (attachment_dict["sid004_owner_type__derived"] == "VCAT"):
##                        rs_attachments_dict[sid004_doc_id]["Owner_Organisation_Name"] = ""
##                        rs_attachments_dict[sid004_doc_id]["Owner_ApplicantContact_First_Name"] = ""
##                        rs_attachments_dict[sid004_doc_id]["Owner_ApplicantContact_Last_Name"] = ""
##                    
##                    rs_attachments_dict[sid004_doc_id]["Objection_Number"] = self.get_string(attachment_dict, "sid004_objection_number")
##                    rs_attachments_dict[sid004_doc_id]["Objection_Withdrawn"] = self.get_string(attachment_dict, "sid004_objection_withdrawn")
##                    
##                    # relate any RA-comments to a referral response.
##                    #
##                    if (attachment_dict["sid004_doctype"] == "RA Response"):
##                        for ra_response_document_id, sid075c_dict in sid075c_dict_dict.iteritems():
##                            if (ra_response_document_id == sid004_doc_id):
##                                rs_attachments_dict[sid004_doc_id]["RA_Comments"] = sid075c_dict["sid075c_comments"]
##                    
##                    # sid005a...
##                    #
##                    sid005a_current_document_doctype = sid005a_dict["sid005a_current_document_doctype"]
##                    
##                    # Note: When a planning permit is being issued applicant attachments are endorsed. 
##                    #       The 'doctype' of some of these attachments goes from "<doctype>" to 
##                    #       "Endorsed <doctype>". 
##                    #
##                    # Note: Some sid005a_dicts will have empty "document_url"s (eg. Application 
##                    #       Acceptance/Rejection).
##                    #
##                    
##                    # Objection - Objectors
##                    #
##                    if (sid005a_current_document_doctype == "Objection"):
##                        rs_attachments_dict[sid004_doc_id]["Online_Objector_First_Name"] = self.get_name_firstname(sid005a_dict, "sid005a_current_document_authenticated_by")
##                        rs_attachments_dict[sid004_doc_id]["Online_Objector_Last_Name"] = self.get_name_lastname(sid005a_dict, "sid005a_current_document_authenticated_by", is_organisation=False)
##                    
##                    rs_attachments_dict[sid004_doc_id]["URL"] = ""
##                    rs_attachments_dict[sid004_doc_id]["Prefix"] = self.get_string(sid005a_dict, "sid005a_current_document_prefix")
##                    rs_attachments_dict[sid004_doc_id]["doc_type"] = self.get_string(sid005a_dict, "sid005a_current_document_doctype")
##                    rs_attachments_dict[sid004_doc_id]["Suffix"] = self.get_string(sid005a_dict, "sid005a_current_document_suffix")
##                    
##                    # Application Acceptance / Rejection - Council
##                    # - Outcome is whole suffix.
##                    #
##                    if (sid005a_current_document_doctype == "Application Acceptance / Rejection"):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Accept_Reject_Outcome"] = self.get_string(sid005a_dict, "sid005a_current_document_suffix")
##                    
##                    # Request for Further Information - Council, RA
##                    # - Subject is 2nd-field (comma-delimited) of suffix.
##                    # - Due date is last field (space-delimited) of suffix.
##                    #
##                    if (sid005a_current_document_doctype == "Request for Further Information"):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Additional_Information_Requested_Subject"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=1)
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Additional_Information_Requested_Due_Date"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
##                    
##                    # Advertising Instructions - Council
##                    # - Section number is the last field (space-delimited) of suffix.
##                    # - Outcome is 2nd-field (comma-delimited) of suffix.
##                    #
##                    if (sid005a_current_document_doctype == "Advertising Instructions"):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Advertising_Instructions_Section"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Advertising_Instructions_Outcome"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=1)
##                    
##                    # Advertising Notice to Referral Authorities - Council
##                    # - Section number is the last field (space-delimited) of suffix.
##                    #
##                    if (sid005a_current_document_doctype == "Advertising Notice to Referral Authorities"):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Advertising_Notice_to_Referral_Authorities_Section"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
##                    
##                    # Arborist report - Applicant
##                    # - Number of pages is last field (space-delimited) of suffix.
##                    #
##                    if (sid005a_current_document_doctype.find("Arborist report") > -1):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Arborist_Report_Number_of_Pages"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
##                    
##                    # Compiled Plan Sheets - Applicant
##                    # - Number of pages is last field (space-delimited) of suffix.
##                    #
##                    if (sid005a_current_document_doctype.find("Compiled Plan Sheets") > -1):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Compiled_Plan_Sheets_Number_of_Pages"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
##                    
##                    # Copy of Title - Applicant
##                    # - Vol/Fol are all but 1st-field (space-delimited) of suffix.
##                    #
##                    if (sid005a_current_document_doctype.find("Copy of Title") > -1):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Copy_of_Title_VolFol"] = self.get_string_multipart(sid005a_dict, "sid005a_current_document_suffix", separator=' ', start_index=1, end_index=-1)
##                    
##                    # Decision - Amended Planning Permit - Council
##                    # - Outcome is 2nd field (comma-delimited) of suffix.
##                    # - Required Action for Endorsement is 3rd field (comma-delimited) of suffix, if present.
##                    #
##                    if (sid005a_current_document_doctype.find("Decision - Amended Planning Permit") > -1):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Decision_Amended_Planning_Permit_Outcome"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=1)
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Decision_Amended_Planning_Permit_Required_Action_for_Endorsement"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=2)
##                        
##                    # Decision - Planning Permit - Council
##                    # - Outcome is 2nd field (comma-delimited) of suffix.
##                    # - Required Action for Endorsement is 3rd field (comma-delimited) of suffix, if present.
##                    #
##                    if (sid005a_current_document_doctype == "Decision - Planning Permit"):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Decision_Planning_Permit_Outcome"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=1)
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Decision_Planning_Permit_Required_Action_for_Endorsement"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=2)
##                    
##                    # Extension of Time - Applicant
##                    # - Subject is the 2nd-fields (comma-delimited) of suffix.
##                    # - Number of days is the last field (space-delimited) of suffix.
##                    #
##                    if (sid005a_current_document_doctype.find("Extension of Time") > -1):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Extension_of_Time_Subject"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=',', index=1)
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Extension_of_Time_Number_of_Days"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
##                    
##                    # Other Document Type - Applicant, Council, RA
##                    # - Whole of suffix.
##                    #
##                    if (sid005a_current_document_doctype.find("Other Document Type") > -1):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Other_Document_Type_Doctype_Description"] = self.get_string(sid005a_dict, "sid005a_current_document_suffix")
##                    
##                    # Plan of Demolition - Applicant
##                    # - Number of pages is last field (space-delimited) of suffix.
##                    # 
##                    if (sid005a_current_document_doctype.find("Plan of Demolition") > -1):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Plan_of_Demolition_Number_of_Pages"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
##                    
##                    # Plan for Endorsement / Endorsed Plan - Applicant
##                    # - Surveyor's Plan Version is the last field (space-delimited) of suffix.
##                    #
##                    if (sid005a_current_document_doctype == "Endorsed Plan") or \
##                       (sid005a_current_document_doctype == "Plan for Endorsement"):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Plan_for_Endorsement_Surveyor_Plan_Version"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
##                    
##                    # Plan of Subdivision or Consolidation - Applicant
##                    # - Number of pages is 4th-field (space-delimited) of suffix. Must trim comma from end.
##                    # - Surveyor's Plan Version is last field (space-delimited) of suffix. 
##                    #
##                    if (sid005a_current_document_doctype.find("Plan of Subdivision or Consolidation") > -1):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Plan_of_Subdivision_or_Consolidation_Number_of_Pages"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=3).replace(",", "")
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Plan_of_Subdivision_or_Consolidation_Surveyor_Plan_Version"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
##                        
##                        if (sid005a_dict["sid005a_current_document_suffix"].find("modified at LV request") > -1):
##                            rs_attachments_dict[sid004_doc_id]["Suffix_Plan_of_Subdivision_or_Consolidation_Modified_LV_Request"] = "modified at LV request"
##                    
##                    # Pre-approved RA Consent - Applicant
##                    #
##                    if (sid005a_current_document_doctype.find("Pre-approved RA Consent") > -1):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Pre-Approved_RA_Consent_RA_Name"] = self.get_string(sid005a_dict, "sid005a_current_document_suffix")
##                    
##                    # Proposed Plan (LV Modifications) - Applicant
##                    #
##                    if (sid005a_current_document_doctype.find("Proposed Plan (LV Modifications)") > -1):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Proposed_Plan_LV_Modifications_Surveyor_Plan_Version"] = self.get_suffix_surveyors_plan_version(sid005a_dict, "sid005a_current_document_suffix")
##                    
##                    # Proposed Plan of Subdivision for Re-Certification - Applicant
##                    #
##                    if (sid005a_current_document_doctype.find("Proposed Plan of Subdivision for Re-Certification") > -1):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Proposed_Plan_of_Subdivision_for_Re-Certification_Surveyor_Plan_Version"] = self.get_suffix_surveyors_plan_version(sid005a_dict, "sid005a_current_document_suffix")
##                    
##                    # RA Response - Council (on behalf of RA), RA
##                    # - Date is 1st-field (space-delimited) of suffix.
##                    # - Time is 2nd-field (space-delimited) of suffix. Must trim comma from end.
##                    # - Outcome is after the 1st-field (comma-delimited) of suffix.
##                    #
##                    if (sid005a_current_document_doctype == "RA Response"):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_RA_Response_Referral_Date"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=0)
##                        rs_attachments_dict[sid004_doc_id]["Suffix_RA_Response_Referral_Time"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=1).replace(",", "")
##                        
##                        if (sid005a_dict["sid005a_current_document_suffix"].find("Section 55") > -1):
##                            rs_attachments_dict[sid004_doc_id]["Suffix_RA_Response_Section_55_Abridged_Response_Outcome"] = self.get_suffix_section55_outcome(sid005a_dict, "sid005a_current_document_suffix")
##                        
##                        if (sid005a_dict["sid005a_current_document_suffix"].find("Certification") > -1):
##                            rs_attachments_dict[sid004_doc_id]["Suffix_RA_Response_Section_8_Cert_Abridged_Response_Outcome"] = self.get_suffix_section8Cert_outcome(sid005a_dict, "sid005a_current_document_suffix")
##                        
##                        if (sid005a_dict["sid005a_current_document_suffix"].find("SOC") > -1):
##                            rs_attachments_dict[sid004_doc_id]["Suffix_RA_Response_Section_8_SOC_Abridged_Response_Outcome"] = self.get_suffix_section8SOC_outcome(sid005a_dict, "sid005a_current_document_suffix")
##                    
##                    # Response to Request for Further Information - Applicant
##                    # - Subject is all fields after first comma of suffix.
##                    #
##                    if (sid005a_current_document_doctype.find("Response to Request for Further Information") > -1):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Response_to_Additional_Information_Request_Subject"] = self.get_string_multipart(sid005a_dict, "sid005a_current_document_suffix", separator=',', start_index=1, end_index=-1)
##                    
##                    # Site plan (proposed) - Applicant
##                    #
##                    if (sid005a_current_document_doctype.find("Site plan (proposed)") > -1):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Site_Plan_Proposed_Number_of_Pages"] = self.get_suffix_number_of_pages(sid005a_dict, "sid005a_current_document_suffix")
##                    
##                    # Submission to Advertising Notice - RA
##                    # - Section number is the last field (space-delimited) of suffix.
##                    #
##                    if (sid005a_current_document_doctype == "Submission to Advertising Notice"):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Submission_to_Advertising_Notice_Section"] = self.get_string_part(sid005a_dict, "sid005a_current_document_suffix", separator=' ', index=-1)
##                    
##                    # Surveyor's Report - Applicant
##                    #
##                    if (sid005a_current_document_doctype.find("Surveyor's Report") > -1):
##                        rs_attachments_dict[sid004_doc_id]["Suffix_Surveyors_Report_Number_of_Pages"] = self.get_suffix_number_of_pages(sid005a_dict, "sid005a_current_document_suffix")
##                    
##                    rs_attachments_dict[sid004_doc_id]["Size"] = self.get_string(sid005a_dict, "sid005a_current_document_size")
##                    rs_attachments_dict[sid004_doc_id]["Format"] = self.get_string(sid005a_dict, "sid005a_current_document_format")
##                    rs_attachments_dict[sid004_doc_id]["Version_Number"] = self.get_string(sid005a_dict, "sid005a_current_document_version_number")
##                    rs_attachments_dict[sid004_doc_id]["Version_Description"] = self.get_string(sid005a_dict, "sid005a_current_document_version_description")
##                    rs_attachments_dict[sid004_doc_id]["Submitted_Date"] = self.get_string(sid005a_dict, "sid005a_current_document_submitted_date")
##                    rs_attachments_dict[sid004_doc_id]["Authenticated_By_First_Name"] = self.get_name_firstname(sid005a_dict, "sid005a_current_document_authenticated_by")
##                    rs_attachments_dict[sid004_doc_id]["Authenticated_By_Last_Name"] = self.get_name_lastname(sid005a_dict, "sid005a_current_document_authenticated_by", is_organisation=True)
##                    rs_attachments_dict[sid004_doc_id]["Authenticated_By_Organisation_Name"] = self.get_name_organisation(sid005a_dict, "sid005a_current_document_authenticated_by")
#        
#        # sid006a...
#        #
#        rs_application_dict["Supplied_By_First_Name"] = self.get_name_firstname(sid006a_dict, "sid006a_supplied_by")
#        rs_application_dict["Supplied_By_Last_Name"] = self.get_name_lastname(sid006a_dict, "sid006a_supplied_by", is_organisation=False)
#        rs_application_dict["Submitted_Date"] = self.get_string(sid006a_dict, "sid006a_submitted_date")
#        rs_application_dict["Modified_By_First_Name"] = self.get_name_firstname(sid006a_dict, "sid006a_modified_by")
#        rs_application_dict["Modified_By_Last_Name"] = self.get_name_lastname(sid006a_dict, "sid006a_modified_by", is_organisation=True)
#        rs_application_dict["Modified_By_Organisation_Name"] = self.get_name_organisation(sid006a_dict, "sid006a_modified_by")
#        rs_application_dict["Modified_Date"] = self.get_string(sid006a_dict, "sid006a_modified_date")
#        rs_application_dict["Application_Type"] = self.get_string(sid006a_dict, "sid006a_application_type")
#        rs_application_dict["Version_Number"] = self.get_integer(sid006a_dict, "sid006a_version_number")
#        rs_application_dict["Previous_SPEAR_Reference_Number"] = self.get_string(sid006a_dict, "sid006a_previous_spear_reference_number")
#        rs_application_dict["Previous_Planning_Permit_Number"] = self.get_string(sid006a_dict, "sid006a_previous_planning_permit_number")
#        rs_application_dict["Previous_Responsible_Authority_Reference_Number"] = self.get_string(sid006a_dict, "sid006a_previous_responsible_authority_reference_number")
#        rs_application_dict["Previous_ApplicantContact_First_Name"] = self.get_name_firstname(sid006a_dict, "sid006a_previous_applicant_contact")
#        rs_application_dict["Previous_ApplicantContact_Last_Name"] = self.get_name_lastname(sid006a_dict, "sid006a_previous_applicant_contact", is_organisation=False)
#        rs_application_dict["Planning_Permit_Requirement"] = self.get_string(sid006a_dict, "sid006a_planning_permit_requirement")
#        rs_application_dict["Council_Organisation_Name"] = self.get_string(sid006a_dict, "sid006a_council_org_name")
#        rs_application_dict["Plan_Number"] = self.get_string(sid006a_dict, "sid006a_plan_number")
#        rs_application_dict["ApplicantContact_Name_Title"] = self.get_name_title(sid006a_dict, "sid006a_applicant_contact_name")
#        rs_application_dict["ApplicantContact_Name_First_Name"] = self.get_name_firstname(sid006a_dict, "sid006a_applicant_contact_name")
#        rs_application_dict["ApplicantContact_Name_Last_Name"] = self.get_name_lastname(sid006a_dict, "sid006a_applicant_contact_name", is_organisation=False)
#        rs_application_dict["ApplicantContact_Organisation_Name"] = self.get_string(sid006a_dict, "sid006a_applicant_contact_organisation_name")
#        rs_application_dict["ApplicantContact_Address_Details_Unit_No"] = self.get_address_unit_no(sid006a_dict, "sid006a_applicant_contact_address_details")
#        rs_application_dict["ApplicantContact_Address_Details_Street_No"] = self.get_address_street_no(sid006a_dict, "sid006a_applicant_contact_address_details")
#        rs_application_dict["ApplicantContact_Address_Details_Street_Name"] = self.get_address_street_name(sid006a_dict, "sid006a_applicant_contact_address_details")
#        rs_application_dict["ApplicantContact_Address_Details_Street_Type"] = self.get_address_street_type(sid006a_dict, "sid006a_applicant_contact_address_details")
#        rs_application_dict["ApplicantContact_Address_Details_Suburb"] = self.get_address_suburb(sid006a_dict, "sid006a_applicant_contact_address_details")
#        rs_application_dict["ApplicantContact_Address_Details_State"] = self.get_address_state(sid006a_dict, "sid006a_applicant_contact_address_details")
#        rs_application_dict["ApplicantContact_Address_Details_Postcode"] = self.get_address_postcode(sid006a_dict, "sid006a_applicant_contact_address_details")
#        rs_application_dict["ApplicantContact_Address_Details_Country"] = self.get_address_country(sid006a_dict, "sid006a_applicant_contact_address_details")
#        rs_application_dict["ApplicantContact_Business_Phone_Area_Code"] = self.get_phone_area_code(sid006a_dict, "sid006a_applicant_contact_business_phone")
#        rs_application_dict["ApplicantContact_Business_Phone_Number"] = self.get_phone_number(sid006a_dict, "sid006a_applicant_contact_business_phone")
#        rs_application_dict["ApplicantContact_Email"] = self.get_string(sid006a_dict, "sid006a_applicant_contact_e-mail")
#        
#        
#        
#        if (sid006a_dict["sid006a_applicant_name"] == "(Applicant details per Applicant Contact)"):
#            rs_application_dict["Applicant_Organisation_Name"] = self.get_string(sid006a_dict, "sid006a_applicant_contact_organisation_name")
#            
#            rs_application_dict["Applicant_Name_Title"] = self.get_name_title(sid006a_dict, "sid006a_applicant_contact_name")
#            rs_application_dict["Applicant_Name_First_Name"] = self.get_name_firstname(sid006a_dict, "sid006a_applicant_contact_name")
#            rs_application_dict["Applicant_Name_Last_Name"] = self.get_name_lastname(sid006a_dict, "sid006a_applicant_contact_name", is_organisation=False)
#            
#            rs_application_dict["Applicant_Address_Details_Unit_No"] = self.get_address_unit_no(sid006a_dict, "sid006a_applicant_contact_address_details")
#            rs_application_dict["Applicant_Address_Details_Street_No"] = self.get_address_street_no(sid006a_dict, "sid006a_applicant_contact_address_details")
#            rs_application_dict["Applicant_Address_Details_Street_Name"] = self.get_address_street_name(sid006a_dict, "sid006a_applicant_contact_address_details")
#            rs_application_dict["Applicant_Address_Details_Street_Type"] = self.get_address_street_type(sid006a_dict, "sid006a_applicant_contact_address_details")
#            rs_application_dict["Applicant_Address_Details_Suburb"] = self.get_address_suburb(sid006a_dict, "sid006a_applicant_contact_address_details")
#            rs_application_dict["Applicant_Address_Details_State"] = self.get_address_state(sid006a_dict, "sid006a_applicant_contact_address_details")
#            rs_application_dict["Applicant_Address_Details_Postcode"] = self.get_address_postcode(sid006a_dict, "sid006a_applicant_contact_address_details")
#            rs_application_dict["Applicant_Address_Details_Country"] = self.get_address_country(sid006a_dict, "sid006a_applicant_contact_address_details")
#        else:
#            rs_application_dict["Applicant_Organisation_Name"] = self.get_string(sid006a_dict, "sid006a_applicant_organisation_name")
#            
#            rs_application_dict["Applicant_Name_Title"] = self.get_name_title(sid006a_dict, "sid006a_applicant_name")
#            rs_application_dict["Applicant_Name_First_Name"] = self.get_name_firstname(sid006a_dict, "sid006a_applicant_name")
#            rs_application_dict["Applicant_Name_Last_Name"] = self.get_name_lastname(sid006a_dict, "sid006a_applicant_name", is_organisation=False)
#
#            rs_application_dict["Applicant_Address_Details_Unit_No"] = self.get_address_unit_no(sid006a_dict, "sid006a_applicant_address_details")
#            rs_application_dict["Applicant_Address_Details_Street_No"] = self.get_address_street_no(sid006a_dict, "sid006a_applicant_address_details")
#            rs_application_dict["Applicant_Address_Details_Street_Name"] = self.get_address_street_name(sid006a_dict, "sid006a_applicant_address_details")
#            rs_application_dict["Applicant_Address_Details_Street_Type"] = self.get_address_street_type(sid006a_dict, "sid006a_applicant_address_details")
#            rs_application_dict["Applicant_Address_Details_Suburb"] = self.get_address_suburb(sid006a_dict, "sid006a_applicant_address_details")
#            rs_application_dict["Applicant_Address_Details_State"] = self.get_address_state(sid006a_dict, "sid006a_applicant_address_details")
#            rs_application_dict["Applicant_Address_Details_Postcode"] = self.get_address_postcode(sid006a_dict, "sid006a_applicant_address_details")
#            rs_application_dict["Applicant_Address_Details_Country"] = self.get_address_country(sid006a_dict, "sid006a_applicant_address_details")
#        
#        if (len(sid006a_dict["sid006a_proposal_type"]) > 0):
#            if (sid006a_dict["sid006a_proposal_type"][0].startswith("Planning permit") is True):
#                rs_application_dict["Application_Classification"] = "Planning"
#                rs_application_dict["Proposal_Type_Subdivision_Dealing_Type_Description"] = ""
#                rs_application_dict["Proposal_Type_Subdivision_No_of_Lots"] = 0
#                rs_application_dict["Proposal_Type_Subdivision_Stage_Number"] = ""
#                rs_application_dict["Proposal_Type_Trigger_List"] = self.get_proposal_type_trigger_tiers_list(sid006a_dict, "sid006a_proposal_type")
#            else:
#                rs_application_dict["Application_Classification"] = "Subdivision"
#                
#                if (sid006a_dict["sid006a_proposal_type"][0].find("lots") > -1):
#                    rs_application_dict["Proposal_Type_Subdivision_No_of_Lots"] = self.get_proposal_type_subdivision_no_of_lots(sid006a_dict, "sid006a_proposal_type")
#                if (sid006a_dict["sid006a_proposal_type"][0].find("Stage") > -1):
#                    rs_application_dict["Proposal_Type_Subdivision_Stage_Number"] = self.get_proposal_type_subdivision_stage_number(sid006a_dict, "sid006a_proposal_type")
#                
#                rs_application_dict["Proposal_Type_Trigger_List"] = []
#            
#            if (sid006a_dict["sid006a_proposal_type"][0].startswith("Dealing Type")):
#                rs_application_dict["Proposal_Type_Subdivision_Dealing_Type_Description"] = self.get_proposal_type_dealingtype_description(sid006a_dict, "sid006a_proposal_type")
#        
#        rs_application_dict["Proposed_Use"] = self.get_string(sid006a_dict, "sid006a_proposed_use")
#        rs_application_dict["Current_Use"] = self.get_string(sid006a_dict, "sid006a_current_use")
#        rs_application_dict["Estimated_Development_Cost"] = self.get_string(sid006a_dict, "sid006a_estimated_development_cost")
#        rs_application_dict["Covenant_Declaration"] = self.get_string(sid006a_dict, "sid006a_covenant_declaration")
#        rs_application_dict["Covenant_Declaration_Comment"] = self.get_string(sid006a_dict, "sid006a_covenant_declaration_comment")
#        rs_application_dict["CHMP_Declaration"] = self.get_string(sid006a_dict, "sid006a_chmp_declaration")
#        rs_application_dict["Authorised_By_First_Name"] = self.get_name_firstname(sid006a_dict, "sid006a_authorised_by")
#        rs_application_dict["Authorised_By_Last_Name"] = self.get_name_lastname(sid006a_dict, "sid006a_authorised_by", is_organisation=False)
#        rs_application_dict["Authorised_Applicant_Organisation_Name"] = self.get_string(sid006a_dict, "sid006a_applicant_organisation_name")
#        
#        rs_application_dict["Owner"] = []
#        for i in range(0, self.get_listlength(sid006a_dict, "sid006a_owner_dict_list")):
#            rs_application_dict["Owner"].append({})
#            
#            if (sid006a_dict["sid006a_owner_dict_list"][i]["sid006a_owner_name"] == "(Owner details per Applicant)"):
#                rs_application_dict["Owner"][-1]["Organisation_Name"] = self.get_string(sid006a_dict, "sid006a_applicant_organisation_name")
#                
#                rs_application_dict["Owner"][-1]["Name_Title"] = self.get_name_title(sid006a_dict, "sid006a_applicant_name")
#                rs_application_dict["Owner"][-1]["Name_First_Name"] = self.get_name_firstname(sid006a_dict, "sid006a_applicant_name")
#                rs_application_dict["Owner"][-1]["Name_Last_Name"] = self.get_name_lastname(sid006a_dict, "sid006a_applicant_name", is_organisation=False)
#                
#                rs_application_dict["Owner"][-1]["Address_Details_Unit_No"] = self.get_address_unit_no(sid006a_dict, "sid006a_applicant_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_Street_No"] = self.get_address_street_no(sid006a_dict, "sid006a_applicant_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_Street_Name"] = self.get_address_street_name(sid006a_dict, "sid006a_applicant_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_Street_Type"] = self.get_address_street_type(sid006a_dict, "sid006a_applicant_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_Suburb"] = self.get_address_suburb(sid006a_dict, "sid006a_applicant_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_State"] = self.get_address_state(sid006a_dict, "sid006a_applicant_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_Postcode"] = self.get_address_postcode(sid006a_dict, "sid006a_applicant_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_Country"] = self.get_address_country(sid006a_dict, "sid006a_applicant_address_details")
#                
#            elif (sid006a_dict["sid006a_owner_dict_list"][i]["sid006a_owner_name"] == "(Owner details per Applicant Contact)"):
#                rs_application_dict["Owner"][-1]["Organisation_Name"] = self.get_string(sid006a_dict, "sid006a_applicant_contact_organisation_name")
#                
#                rs_application_dict["Owner"][-1]["Name_Title"] = self.get_name_title(sid006a_dict, "sid006a_applicant_contact_name")
#                rs_application_dict["Owner"][-1]["Name_First_Name"] = self.get_name_firstname(sid006a_dict, "sid006a_applicant_contact_name")
#                rs_application_dict["Owner"][-1]["Name_Last_Name"] = self.get_name_lastname(sid006a_dict, "sid006a_applicant_contact_name", is_organisation=False)
#                
#                rs_application_dict["Owner"][-1]["Address_Details_Unit_No"] = self.get_address_unit_no(sid006a_dict, "sid006a_applicant_contact_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_Street_No"] = self.get_address_street_no(sid006a_dict, "sid006a_applicant_contact_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_Street_Name"] = self.get_address_street_name(sid006a_dict, "sid006a_applicant_contact_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_Street_Type"] = self.get_address_street_type(sid006a_dict, "sid006a_applicant_contact_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_Suburb"] = self.get_address_suburb(sid006a_dict, "sid006a_applicant_contact_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_State"] = self.get_address_state(sid006a_dict, "sid006a_applicant_contact_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_Postcode"] = self.get_address_postcode(sid006a_dict, "sid006a_applicant_contact_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_Country"] = self.get_address_country(sid006a_dict, "sid006a_applicant_contact_address_details")
#                
#            else:
#                rs_application_dict["Owner"][-1]["Organisation_Name"] = self.get_string(sid006a_dict, "sid006a_owner_organisation_name")
#                
#                rs_application_dict["Owner"][-1]["Name_Title"] = self.get_name_title(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_name")
#                rs_application_dict["Owner"][-1]["Name_First_Name"] = self.get_name_firstname(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_name")
#                rs_application_dict["Owner"][-1]["Name_Last_Name"] = self.get_name_lastname(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_name", is_organisation=False)
#                
#                rs_application_dict["Owner"][-1]["Address_Details_Unit_No"] = self.get_address_unit_no(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_Street_No"] = self.get_address_street_no(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_Street_Name"] = self.get_address_street_name(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_Street_Type"] = self.get_address_street_type(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_Suburb"] = self.get_address_suburb(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_State"] = self.get_address_state(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_Postcode"] = self.get_address_postcode(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_address_details")
#                rs_application_dict["Owner"][-1]["Address_Details_Country"] = self.get_address_country(sid006a_dict["sid006a_owner_dict_list"][i], "sid006a_owner_address_details")
#            
#        rs_application_dict["Property"] = []
#        for i in range(0, self.get_listlength(sid006a_dict, "sid006a_property_dict_list")):
#            rs_application_dict["Property"].append({})
#            
#            rs_application_dict["Property"][-1]["Address_Unit_No"] = self.get_address_unit_no(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_address")
#            rs_application_dict["Property"][-1]["Address_Street_No"] = self.get_address_street_no(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_address")
#            rs_application_dict["Property"][-1]["Address_Street_Name"] = self.get_address_street_name(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_address")
#            rs_application_dict["Property"][-1]["Address_Street_Type"] = self.get_address_street_type(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_address")
#            rs_application_dict["Property"][-1]["Address_Street_Suffix"] = self.get_address_street_suffix(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_address")
#            rs_application_dict["Property"][-1]["Address_Suburb"] = self.get_address_suburb(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_address")
#            rs_application_dict["Property"][-1]["Address_State"] = self.get_address_state(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_address")
#            rs_application_dict["Property"][-1]["Address_Postcode"] = self.get_address_postcode(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_address")
#            rs_application_dict["Property"][-1]["Address_Country"] = self.get_address_country(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_address")
#            rs_application_dict["Property"][-1]["CPN"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_cpn")
#            rs_application_dict["Property"][-1]["Lot_Number"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_lot_number")
#            rs_application_dict["Property"][-1]["Plan_Number"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_plan_number")
#            rs_application_dict["Property"][-1]["No_VolFol"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_no_volfol")
#            rs_application_dict["Property"][-1]["Volume_Number"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_volume_number")
#            rs_application_dict["Property"][-1]["Folio_Number"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_folio_number")
#            rs_application_dict["Property"][-1]["Crown_Allotment_No"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_crown_allotment_no")
#            rs_application_dict["Property"][-1]["Section_No"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_section_no")
#            rs_application_dict["Property"][-1]["Parish_Name"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_parish_name")
#            rs_application_dict["Property"][-1]["SPI"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_spi")
#            rs_application_dict["Property"][-1]["Other"] = self.get_string(sid006a_dict["sid006a_property_dict_list"][i], "sid006a_property_other")
#        
#        # sid061a...
#        #
#        for ra_request_document_id, sid061a_dict in sid061a_dict_dict.iteritems():
#            for ra_dict in sid061a_dict["sid061a_ra_dict_dict"].itervalues():
#                d = {}
#                d["ReferralRequest_Date"] = self.get_string(sid061a_dict, "sid061a_date")
#                d["ReferralRequest_Time"] = self.get_string(sid061a_dict, "sid061a_time")
#                d["ReferralRequest_Supplied_By"] = self.get_string(sid061a_dict, "sid061a_supplied_by")
#                d["RA_Name"] = self.get_string(ra_dict, "sid061a_ra_name")
#                d["RA_Requirements_Section_55_Flag"] = (self.get_string(ra_dict, "sid061a_ra_requirements") == "Section 55")
#                d["RA_Requirements_Section_8_Flag"] = (self.get_string(ra_dict, "sid061a_ra_requirements") == "Section 8")
#                d["RA_Requirements_Section_55_8_Flag"] = (self.get_string(ra_dict, "sid061a_ra_requirements") == "Section 8 &amp; Section 55")
#                d["RA_Requirements_For_Info_Flag"] = (self.get_string(ra_dict, "sid061a_ra_requirements") == "For information only")
#                d["Council_RA_Comments"] = self.get_string(ra_dict, "sid061a_council_ra_comments")
#                d["referral_request_document_id"] = ra_request_document_id
#                d["referral_response_document_id_list"] = []
#                
#                # Find RA responses for this request...
#                #
#                # Note: A "Section 55 & 8" referral request can have separate 
#                #       responses from each RA. Therefore we must store a list
#                #       of response ids per request.
#                #
#                
#                for ra_response_document_id, sid005a_dict in sid005a_dict_dict.iteritems():
#                    if ("sid005a_ra_request_document_id_list" in sid005a_dict) and \
#                       (ra_request_document_id in sid005a_dict["sid005a_ra_request_document_id_list"]):
#                        d["referral_response_document_id_list"].append(ra_response_document_id)
#                
#                rs_referrals_dict_list.append(d)
#            
#        # sid072a...
#        #
#        rs_application_dict["Reference_Number_1"] = self.get_string(sid072a_dict, "sid072a_reference_number_1")
#        rs_application_dict["Reference_Number_2"] = self.get_string(sid072a_dict, "sid072a_reference_number_2")
#        rs_application_dict["Delegate_Planning_Officer_First_Name"] = self.get_name_firstname(sid072a_dict, "sid072a_delegate_planning_officer_full_name")
#        rs_application_dict["Delegate_Planning_Officer_Last_Name"] = self.get_name_lastname(sid072a_dict, "sid072a_delegate_planning_officer_full_name", is_organisation=False)
#        
#        app_rs.set_var("application", rs_application_dict)
#        app_rs.set_var("referrals", rs_referrals_dict_list)
#        app_rs.set_var("attachments", rs_attachments_dict)
#        app_rs.set_var("mandatory_action_list", rs_mandatory_action_list)
#        app_rs.set_var("optional_action_list", rs_optional_action_list)
        
    def get_string(self, source_dict, key):
        """
        Return the value of the key 'key' from the argument dictionary 'source_dict'.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String
        """
        
        return_string = ""
        
        if (source_dict is not None) and (key in source_dict):
            return_string = source_dict[key]
            
            self.logger.log(NodeLogger.TRACE, "source string (%s), return_string (%s)" % (source_dict[key], return_string))
            
        return return_string
    
    def get_stringlength(self, source_dict, key):
        """
        Return the string length of the value of the key 'key' from the argument
        dictionary 'source_dict'.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String
        """
        
        return len(self.get_string(source_dict, key))
    
    def get_string_part(self, source_dict, key, separator=' ', index=0):
        """
        Return the nth ('index') value of the key 'key' from the argument 
        dictionary 'source_dict', based on the argument 'separator'.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :param separator: String - Separator String to split element
        :param index: int - the index of the split element to return
        :rtype: String - the split element part.
        """
        
        return_string = ""
        
        if (source_dict is not None) and (key in source_dict):
            parts = source_dict[key].split(separator)
            if (len(parts) > index):
                return_string = parts[index]
            
            self.logger.log(NodeLogger.TRACE, "source string (%s), return_string (%s)" % (source_dict[key], return_string))
            
        return return_string
    
    def get_string_multipart(self, source_dict, key, separator=' ', start_index=0, end_index=0):
        """
        Return multiple contiguous parts of the value of the key 'key' of the
        argument dictionary 'source_dict', based on the argument 'separator'.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :param separator: String - Separator String to split element
        :param start_index: Integer - the start index of the split element to return
        :param end_index: Integer - the final index of the split element to return
        :rtype: String - the split element parts concatenated.
        """
        
        return_string = ""
        
        if (source_dict is not None) and (key in source_dict):
            parts = source_dict[key].split(separator)
            if (len(parts) > start_index):
                if (end_index == -1):
                    end_index = len(parts) - 1
                
                if (start_index < end_index):
                    return_string = separator.join(parts[start_index:end_index])
                else:
                    return_string = parts[start_index]
            
        return return_string
    
    def get_integer(self, source_dict, key):
        """
        Return the value, as an integer, of the key 'key' from the argument 
        dictionary 'source_dict'.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: int
        """
        
        return_integer = 0
        
        if (source_dict is not None) and (key in source_dict):
            return_integer = int(source_dict[key])
            
            self.logger.log(NodeLogger.TRACE, "source string (%s), return_integer (%s)" % (source_dict[key], return_integer))
            
        return return_integer
    
    def get_list(self, source_dict, key):
        """
        Return the value, as an list, of the key 'key' from the argument 
        dictionary 'source_dict'.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: List
        """
        
        return_list = []
        
        if (source_dict is not None) and (key in source_dict):
            return_list = source_dict[key]
        
        return return_list
    
    def get_listlength(self, source_dict, key):
        """
        Return the length of the value list of the key 'key' from the argument
        dictionary 'source_dict'.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the list length
        """
        
        return len(self.get_list(source_dict, key))
    
    def get_name_title(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a person's name, return the person's title (eg. 'Mr', 'Ms', etc). Only
        the first part (based on space separator) of the name is interpreted as 
        the title and it must exist in the static 'titles' list loaded from the 
        service configuration file (via Spaces).
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the title
        """
        
        return_string = ""
        
        if (source_dict is not None) and (key in source_dict):
            parts = source_dict[key].split(' ')
            
            # return an empty string if the first part does not match a pre-defined title.
            #
            if (len(parts) > 0) and \
               (parts[0].upper() in self.statics.get_var_part('content.titles')):
                return_string = parts[0]
        
            self.logger.log(NodeLogger.TRACE, "source string (%s), name title (%s)" % (source_dict[key], return_string))
        
        return return_string
    
    def get_name_firstname(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a person's name, return the person's first name. If the first part 
        (based on space separator) of the name is a title then the second part
        is returned, otherwise the first part is returned.  
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the first name
        """
        
        return_string = ""
        
        if (source_dict is not None) and (key in source_dict):
            parts = source_dict[key].split(' ')
        
            # Note: We assume the first name is one word only (ie. no internal spaces)
            #
            
            if (len(parts) > 0):
                # first part should match one of the titles.
                #
                title = self.get_name_title(source_dict, key)
                if (len(title) > 0):
                    if (len(parts) > 1):
                        # title is first part so second part is first name.
                        #
                        return_string = parts[1]
                    else:
                        # title is first and only part so no first name.
                        #
                        return_string = ""
                else:
                    # First part is first name.
                    #
                    return_string = parts[0]
    
            self.logger.log(NodeLogger.TRACE, "source string (%s), first name (%s)" % (source_dict[key], return_string))
        
        return return_string
    
    def get_name_lastname(self, source_dict, key, is_organisation=False):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a person's name, return the person's last name. If the first part 
        (based on space separator) of the name is a title then the second part
        is taken to be the first name, otherwise the first part is taken to be
        the first name. All remaining parts (after the first name and up to any
        organisation name in "()" that may exist) are returned. 
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :param is_organisation: Boolean - True if an organisation name is expected
            in the element
        :rtype: String - the last name
        """
        
        return_string = ""
        
        if (source_dict is not None) and (key in source_dict):
            parts = source_dict[key].split(' ')
            
            # organisation within name string is within "()"
            #
            
            if (len(parts) > 0):
                # first part should match one of the titles.
                #
                title = self.get_name_title(source_dict, key)
                if (len(title) > 0):
                    if (len(parts) > 2):
                        # title is first part and first name is second part so remaining parts before any organisation is last name.
                        #
                        if (is_organisation is True):
                            for p in parts[2:]:
                                if (p.startswith("(") is not True):
                                    return_string += p + ' '
                                else:
                                    break
                        else:
                            return_string = " ".join(parts[2:])
                    else:
                        # title is first part and first name is second part so no last name.
                        #
                        return_string = ""
                
                elif (len(parts) > 1):
                    # first name is first part so remaining parts before any organisation is last name.
                    #
                    if (is_organisation is True):
                        for p in parts[1:]:
                            if (p.startswith("(") is not True):
                                return_string += p + ' '
                            else:
                                break
                    else:
                        return_string = " ".join(parts[1:])
                
            self.logger.log(NodeLogger.TRACE, "source string (%s), last name (%s)" % (source_dict[key], return_string))
        
        return return_string.strip()
    
    def get_name_organisation(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a person's name, return any text that appears within "()". 
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the organisation name
        """
        
        # return the string in "()", if any.
        #
        return_string = ""
        
        if (source_dict is not None) and (key in source_dict):
            bracket_pos = source_dict[key].find("(")
            if (bracket_pos > -1):
                end_bracket_pos = source_dict[key].find(")", bracket_pos + 1)
                if (end_bracket_pos > -1):
                    return_string = source_dict[key][bracket_pos + 1:end_bracket_pos]
                else:
                    return_string = source_dict[key][bracket_pos + 1:]
            
            self.logger.log(NodeLogger.TRACE, "source string (%s), name organisation (%s)" % (source_dict[key], return_string))
        
        return return_string
    
    def get_address_unit_no(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a street address, return all text up to the first '/', if one exists.
        Otherwise return an empty string.
        
        The unit number is not mandatory in Spear addresses.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the unit number
        """
        
        return_string = ""
        
        if (source_dict is not None) and (key in source_dict):
            slash_pos = source_dict[key].find("/")
            if (slash_pos > -1):
                return_string = source_dict[key][:slash_pos]
            
            self.logger.log(NodeLogger.TRACE, "source string (%s), unit no (%s)" % (source_dict[key], return_string))
        
        return return_string
    
    def get_address_street_no(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a street address, return the street number. If present it precedes 
        the street name. Note that we must work backwards from the street name, 
        rather than from the start of the string, because both unit number and 
        street number are not mandatory and we can't recognise a street name.
        Return an empty string if a street number cannot be determined.
        
        The street number is not mandatory in Spear addresses.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the street number
        """
        
        return_string = ""
        
        if (source_dict is not None) and (key in source_dict):
            street_name = self.get_address_street_name(source_dict, key)
            if (len(street_name) > 0):
                source_string = source_dict[key].replace(",", "")
                parts = source_string.split(' ')
                for i in range(0, len(parts)):
                    if (parts[i] == street_name) and \
                       (i > 0):
                        thing = parts[i - 1]
                        slash_pos = thing.find("/")
                        if (slash_pos > -1):
                            return_string = thing[slash_pos + 1:]
                        else:
                            return_string = thing
                        break
                        
            self.logger.log(NodeLogger.TRACE, "source string (%s), street no (%s)" % (source_dict[key], return_string))
        
        return return_string
    
    def get_address_street_name(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a street address, return the street name. If a street type was entered 
        then the street type is immediately before the first comma and the street
        name precedes this. If a street type was not entered then the suburb 
        precedes the first comma and the street name precedes this.
        
        The street name is mandatory in Spear addresses.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the street name
        """
        
        return_string = ""
        
        if (source_dict is not None) and (key in source_dict):
            comma_pos = source_dict[key].find(",")
            if (comma_pos > -1):
                parts = source_dict[key][:comma_pos].split(' ')
                if (len(parts) > 1):
                    if (parts[-1].upper() in self.statics.get_var_part('content.street_types')):
                        return_string = parts[-2]
                    
                    # suburb (mandatory) before comma.
                    #
                    elif (len(parts) > 2):
                        return_string = parts[-3]
                        
            self.logger.log(NodeLogger.TRACE, "source string (%s), street name (%s)" % (source_dict[key], return_string))
        
        return return_string
    
    def get_address_street_type(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a street address, return the street type. If a street type was entered 
        then the street type is immediately before the first comma, unless 
        there is a street suffix. The street type must exist in the static 
        'street_types' list loaded from the service configuration file (via 
        Spaces).
        
        The street type is not mandatory in Spear addresses.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the street type
        """
        
        return_string = ""
        
        if (source_dict is not None) and (key in source_dict):
            comma_pos = source_dict[key].find(",")
            if (comma_pos > -1):
                parts = source_dict[key][:comma_pos].split(' ')
                if (len(parts) > 0):
                    if (parts[-1].upper() in self.statics.get_var_part('content.street_types')):
                        return_string = parts[-1]
                elif (len(parts) > 1):
                    if (parts[-1].upper() in self.statics.get_var_part('content.street_types')):
                        return_string = parts[-1]
                    elif (parts[-2].upper() in self.statics.get_var_part('content.street_types')):
                        return_string = parts[-2]
            
            self.logger.log(NodeLogger.TRACE, "source string (%s), street type (%s)" % (source_dict[key], return_string))
        
        return return_string
    
    def get_address_street_suffix(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a street address, return the street suffix. If a street suffix was 
        entered then the street suffix is immediately before the first comma. 
        A suffix can only exist if a street name has been entered. 
        
        The street suffix is not mandatory in Spear addresses.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the street suffix
        """
        
        return_string = ""
        
        if (source_dict is not None) and (key in source_dict):
            comma_pos = source_dict[key].find(",")
            if (comma_pos > -1):
                parts = source_dict[key][:comma_pos].split(' ')
                
                # there must be at least 2 parts (street name & suffix) for a suffix to exist.
                #
                if (len(parts) > 1):
                    if (parts[-1].upper() not in self.statics.get_var_part('content.street_types')):
                        # part is not a street type so must be a suffix.
                        #
                        return_string = parts[-1]
            
            self.logger.log(NodeLogger.TRACE, "source string (%s), street suffix (%s)" % (source_dict[key], return_string))
        
        return return_string
    
    def get_address_suburb(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a street address, return the suburb. If a street type was entered then 
        the street type is immediately before the first comma and the suburb 
        is/are the first word(s) after the first comma and before the second 
        comma or state name. Otherwise the suburb is the first word before the 
        first comma.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the suburb
        """
        
        return_string = ""
        
        if (source_dict is not None) and (key in source_dict):
            comma_parts = source_dict[key].split(',')
            if (len(comma_parts) > 0):
                space_parts = comma_parts[0].split(' ')
                if (len(space_parts) > 0):
                    if (space_parts[-1].upper() in self.statics.get_var_part('content.street_types')):
                        space_parts = comma_parts[1].split(' ')
                        for i in range(0, len(space_parts)):
                            if (space_parts[i] in self.statics.get_var_part('content.states')):
                                return_string = " ".join(space_parts[:i])
                                break
                        if (len(return_string) == 0):
                            return_string = comma_parts[1].strip()
                    else:
                        return_string = space_parts[-1]

            self.logger.log(NodeLogger.TRACE, "source string (%s), suburb (%s)" % (source_dict[key], return_string))
        
        return return_string
    
    def get_address_state(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a street address, return the state. If we're not expecting a country 
        then postcode may be the last part. However the postcode is not a 
        mandatory field but state IS mandatory and should always appear before it.
        The state must exist in the static 'states' list loaded from the service
        configuration file (via Spaces).
        
        The state is mandatory in Spear addresses.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the state
        """
        
        return_string = ""
        
        if (source_dict is not None) and (key in source_dict):
            source_string = source_dict[key].replace(",", " ")
            parts = source_string.split(' ')
            
            # find the state and return the next part, if any. 
            #
            if (len(parts) > 0):
                for i in range(len(parts) - 1, 0, -1):
                    if (parts[i].upper() in self.statics.get_var_part('content.states')):
                        return_string = parts[i]
                        break
            
            self.logger.log(NodeLogger.TRACE, "source string (%s), state (%s)" % (source_dict[key], return_string))
        
        return return_string
    
    def get_address_postcode(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a street address, return the postcode. The postcode always follows
        the state.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the postcode
        """
        
        return_string = ""
        
        if (source_dict is not None) and (key in source_dict):
            source_string = source_dict[key].replace(",", "")
            parts = source_string.split(' ')
            
            if (len(parts) > 0):
                for i in range(len(parts) - 1, 0, -1):
                    if (parts[i].upper() in self.statics.get_var_part('content.states')):
                        if (i < (len(parts) - 1)):
                            return_string = parts[i + 1]
                            break
            
            self.logger.log(NodeLogger.TRACE, "source string (%s), postcode (%s)" % (source_dict[key], return_string))
        
        return return_string
    
    def get_address_country(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a street address, return the country. The country is the last part
        of the address. The country must exist in the static 'countries' list 
        loaded from the service configuration file (via Spaces).
        
        The country is mandatory in Spear addresses.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the country
        """
        
        return_string = ""
        
        if (source_dict is not None) and (key in source_dict):
            parts = source_dict[key].split(' ')
            
            if (len(parts) > 0) and \
               (parts[-1].upper() in self.statics.get_var_part('content.countries')):
                return_string = parts[-1]
            
            self.logger.log(NodeLogger.TRACE, "source string (%s), country (%s)" % (source_dict[key], return_string))
        
        return return_string
    
    def get_phone_area_code(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a phone number, return the area code. The area code is the first part.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the area code
        """
        
        # area code is part before first space.
        #
        return_string = ""
        
        if (source_dict is not None) and (key in source_dict):
            parts = source_dict[key].split(' ')
            if (len(parts) > 0):
                return_string = parts[0]
        
            self.logger.log(NodeLogger.TRACE, "source string (%s), phone area code (%s)" % (source_dict[key], return_string))
        
        return return_string
    
    def get_phone_number(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a phone number, return the part not including the area code. 
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the phone number
        """
        
        return_string = ""
        
        if (source_dict is not None) and (key in source_dict):
            parts = source_dict[key].split(' ')
            if (len(parts) > 0):
                return_string = " ".join(parts[1:])
        
            self.logger.log(NodeLogger.TRACE, "source string (%s), phone number (%s)" % (source_dict[key], return_string))
        
        return return_string
    
    def get_proposal_type_trigger_tiers_list(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a list of proposal types, break each list elements into "tiers" where
        "Tier1" is all text before the "(" and "Tier2" is all text within "()".
        Return a list of dictionaries, where each dictionary contains "Tier1"
        and "Tier2".
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: List - a list of tiers.
        """
        
        proposal_type_dict_list = []
        
        if (source_dict is not None) and (key in source_dict):
            proposal_type_string_list = source_dict[key]
            
            # sid006a_proposal_type may have just one element (a single tier1/tier2 
            # pair, or many elements where the first simply says "Planning permit to:".
            #
            if (len(proposal_type_string_list) == 1):
                proposal_type_dict_list.append(self.get_proposal_type_trigger_tiers_dict(proposal_type_string_list[0].replace("Planning permit to", "").strip()))
            else:
                # skip the first element - "Planning permit to:"
                #
                for proposal_type in proposal_type_string_list[1:]:
                    proposal_type_dict_list.append(self.get_proposal_type_trigger_tiers_dict(proposal_type))
        
        return proposal_type_dict_list
    
    def get_proposal_type_trigger_tiers_dict(self, proposal_type_string):
        """
        Split the argument into a dictionary of parts:
        
        - "Tier1" is that part before "("
        - "Tier2" is that part within "()"
        
        :param proposal_type_string: String
        :rtype: Dictionary - contains the tiers and values.
        """
        
        proposal_type_dict = {}
        
        bracket_pos = proposal_type_string.find("(")
        if (bracket_pos > -1):
            proposal_type_dict["Tier1"] = proposal_type_string[:bracket_pos]
            end_bracket_pos = proposal_type_string.rfind(")")
            proposal_type_dict["Tier2"] = proposal_type_string[bracket_pos + 1:end_bracket_pos]
        else:
            proposal_type_dict["Tier1"] = proposal_type_string
            proposal_type_dict["Tier2"] = ""
        
        return proposal_type_dict
    
    def get_proposal_type_dealingtype_description(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a "Dealing Type" proposal type, return all text in "()", including
        "()".
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the dealing type description
        """
        
        dealingtype_description = ""
        
        # We use only the first element of the proposal_type list.
        #
        if (source_dict is not None) and (key in source_dict):
            proposal_type_string = source_dict[key][0]
            
            proposal_type = proposal_type_string.replace("Dealing Type","").strip()
            bracket_pos = proposal_type.find(")")
            dealingtype_description = proposal_type[:bracket_pos + 1]
        
        return dealingtype_description
    
    def get_proposal_type_subdivision_no_of_lots(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a proposal type, return the number before the word "lots", if it
        exists. Otherwise return 0.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the number of lots
        """
        
        subdivision_no_of_lots = 0
        
        # We use only the first element of the proposal_type list.
        #
        if (source_dict is not None) and (key in source_dict):
            proposal_type_string = source_dict[key][0]
            
            # "lots" is after the dealing type description and before the stage number, if any.
            #
            parts = proposal_type_string.split()
            for i in range(0, len(parts)):
                if (parts[i] == "lots") and \
                   (i > 0):
                    subdivision_no_of_lots = int(parts[i - 1])
                    break
        
        return subdivision_no_of_lots
    
    def get_proposal_type_subdivision_stage_number(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a proposal type, return the last part.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the stage number
        """
        
        stage_number = ""
        
        # We use only the first element of the proposal_type list.
        #
        if (source_dict is not None) and (key in source_dict):
            proposal_type_string = source_dict[key][0]
            
            # Stage is the last field (space-delimited) of proposal type.
            #
            stage_number = proposal_type_string.split()[-1]
        
        return stage_number
    
    def get_suffix_surveyors_plan_version(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a proposal type, return the part after "Surveyor's Plan Version:",
        if it exists. Otherwise return "".
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the plan version
        """
        
        surveyors_plan_version = ""
        
        if (source_dict is not None) and (key in source_dict):
            suffix_string = source_dict[key]
            
            # version is field after "Surveyor's Plan Version:"
            #
            parts = suffix_string.split(',')
            for i in range(0, len(parts)):
                if (parts[i].startswith("Surveyor's Plan Version:") is True):
                    surveyors_plan_version = parts[i].split()[-1]
                    break
        
        return surveyors_plan_version
    
    def get_suffix_section55_outcome(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a Section 55 suffix, return the outcome for the 'key' section.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the outcome
        """
        
        outcome = ""
        
        self.logger.log(logging.DEBUG, "===== key (%s), value (%s)" % (key, source_dict[key]))
        
        if (source_dict is not None) and (key in source_dict):
            suffix_string = self.get_string_multipart(source_dict, key, separator=',', start_index=1, end_index=-1)
            outcome = self.get_suffix_outcome(suffix_string, "Section 55")
            
        return outcome
    
    def get_suffix_section8Cert_outcome(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a Section 8 certification suffix, return the outcome for the 'key' 
        section.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the outcome
        """
        
        outcome = ""
        
        if (source_dict is not None) and (key in source_dict):
            suffix_string = self.get_string_multipart(source_dict, key, separator=',', start_index=1, end_index=-1)
            outcome = self.get_suffix_outcome(suffix_string, "Certification")
        
        return outcome
    
    def get_suffix_section8SOC_outcome(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a Section 8 SOC suffix, return the outcome for the 'key' section.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the outcome
        """
        
        outcome = ""
        
        if (source_dict is not None) and (key in source_dict):
            suffix_string = self.get_string_multipart(source_dict, key, separator=',', start_index=1, end_index=-1)
            outcome = self.get_suffix_outcome(suffix_string, "SOC")
        
        return outcome
    
    def get_suffix_outcome(self, suffix_string, key):
        """
        Interpreting the argument 'suffix_string' as a document suffix, find the 
        suffix outcome string with the argument 'key' (based on comma separator), 
        and return the part of the suffix outcome before "(". The format of the 
        suffix outcome is::
        
            "<outcome> (Section <number>)"
        
        where <number> is "8" or "55".
        
        :param suffix_string: String
        :param key: String - Key must exist in 'suffix_string' for outcome to be determined.
        :rtype: String - the outcome
        """
        
        return_string = ""
        
        suffix_string_parts = suffix_string.split(',')
        for part in suffix_string_parts:
            if (part.find(key) > -1):
                return_string = part
#                bracket_pos = part.find("(")
#                if (bracket_pos > -1):
#                    return_string = part[:bracket_pos].strip()
#                else:
#                    return_string = part
                break
        
        return return_string
    
    def get_suffix_number_of_pages(self, source_dict, key):
        """
        Interpreting the value of the argument 'source_dict', indexed by 'key', 
        as a document suffix, return the number after "Number of Pages:", or
        an empty string if not found.
        
        :param suffix_string: String
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: String - the number of pages.
        """
        
        number_of_pages = ""
        
        if (source_dict is not None) and (key in source_dict):
            suffix_string = source_dict[key]
            
            # number of pages is field after "Number of Pages:"
            #
            parts = suffix_string.split(',')
            for i in range(0, len(parts)):
                if (parts[i].startswith("Number of Pages:") is True):
                    number_of_pages = parts[i].split()[-1]
                    break
        
        return number_of_pages
    
    def get_size(self, source_dict, key):
        """
        Return the value, as an integer, of the key 'key' from the argument 
        dictionary 'source_dict'. The size will be passed as either:
        
            xxxx KB
            xxxx MB
        
        We will convert the number ('xxxx') to an integer and multiply by 1024
        for 'KB' and (1024*1024) for 'MB'.
        
        :param source_dict: Dictionary - Source of data.
        :param key: String - the key of the element in 'source_dict' to inspect
        :rtype: int
        """
        
        return_size = 0
        
        if (source_dict is not None) and (key in source_dict):
            parts = source_dict[key].split()
            if (len(parts) > 1):
                if (parts[1] == 'KB'):
                    return_size = int(float(parts[0])) * 1024
                elif (parts[1] == 'MB'):
                    return_size = int(float(parts[0])) * 1024 * 1024
                else:
                    return_size = int(float(parts[0]))
            else:
                return_size = int(float(parts[0]))
            
            self.logger.log(NodeLogger.TRACE, "source string (%s), return_size (%s)" % (source_dict[key], return_size))
            
        return return_size
    
class SpearActivityLogParser(HTMLParser):
    """
    Internal class used by A2PService to process Spear activity log.
    
    We use an HTML parser rather than an XML parser for convenience as we need a 
    simple SAX parser and python's HTMLParser is just that.
    """

    def __init__(self, spear_reference_numbers_dict):
        # Constructor
        #
        # Parameters:
        #     attributes :
        #
        
        HTMLParser.__init__(self)
        
        self.logger = logging.getLogger('eplanning.spear.a2p_service.SpearActivityLogParser')
        
        # Note: We use a dictionary to store the spear reference numbers because
        #       this will automatically perform de-duping for us.
        #
        self.spear_reference_numbers_dict = spear_reference_numbers_dict
        self.get_data = False
        
    def handle_starttag(self, tag, attrs):
        """
        This function is called by HTMLParser when an HTML start tag is
        encountered. The tag's name and attributes are passed.
        
        :param tag: String
        :param attrs: List of (name, value) pairs
        """
        
        self.logger.log(NodeLogger.TRACE, "tag (%s) attrs (%s)" % (tag, attrs))
        
        if (tag == "spearreference"):
            self.get_data = True
        
    def handle_endtag(self, tag):
        """
        This function is called by HTMLParser when an HTML end tag is
        encountered. The tag's name is passed.
        
        :param tag: String
        """
        
        self.logger.log(NodeLogger.TRACE, "========  endtag" )
        self.get_data = False
        
    def handle_data(self, data):
        """
        This function is called by HTMLParser when arbitrary data is
        encountered. The data is passed.
        
        :param data: String
        """
        
        if (self.get_data is True) and (data is not None) and (len(data.strip()) > 0):
            self.spear_reference_numbers_dict[data.strip()] = ""
            
            self.logger.log(NodeLogger.TRACE, "saving spear refernece number (%s)" % (data.strip()))
        else:
            self.logger.log(NodeLogger.TRACE, "========  not saving data (%s)" % data.strip())
            
    def handle_comment(self, data):
        """
        This function is called by HTMLParser when an HTML comment is
        encountered. The comment's contents is passed.
        
        :param data: String
        """
        
        self.logger.log(NodeLogger.TRACE, "========  comment" )

    def handle_charref(self, name):
        """
        This function is called by HTMLParser when an HTML character reference
        (ie. "&#..;) is encountered. The reference is passed.
        
        :param name: String
        """
        
        self.logger.log(NodeLogger.TRACE, "========  charref" )
        
    def handle_entityref(self, name):
        """
        This function is called by HTMLParser when a general HTML entity
        reference (ie. &name;) is encountered. The reference is passed.
        
        :param name: String
        """
        
        self.logger.log(NodeLogger.TRACE, "========  entityref" )
    
    def handle_decl(self, decl):
        """
        This function is called by HTMLParser when an SGML declaration is
        encountered. The 'decl' parameter will be the entire contents of the 
        declaration inside the <!...> markup.
        
        :param decl: String
        """
        
        self.logger.log(NodeLogger.TRACE, "========  decl" )
    
    def handle_pi(self, data):
        """
        This function is called by HTMLParser when an SGML processing instruction
        (ie. <?proc color='red'>) is encountered. The 'data' parameter will contain 
        the entire processing instruction.
        
        :param data: String
        """
        
        self.logger.log(NodeLogger.TRACE, "========  pi" )
    
    def finish(self):
        self.logger.log(NodeLogger.TRACE, "========  finish" )
        return self.base